- Attantion
- 记住英文的常用键
- 编程学习的三个阶段
- java语言的特点
- 写代码的好习惯
- Java程序报错时找错
- java环境变量和软件安装
- 第一个Java程序
- Java中Scanner对象的使用
- Java中System.out.print和System.out.println的区别
- 面向对象与基于对象
- java标识符的规则
- 标识符的命名规范
- java注释种类
- 进制转换
- 原码、反码、补码
- java是强类型语言(对数据类型的要求十分严格)
- Java中常量分类
- java数据类型
- 如何记忆10000,以及11111的值
- 如何快速计算二进制数
- 数据类型转换
- 类型转换举例
- 运算符/和%的特殊
- 数据溢出
- +的两种使用方法
- 自增运算符
- 特殊+和赋值运算结合
- &与&&都可以表示逻辑与运算,区别
- 位运算(直接操作比特位,补码)
- 移位特殊情况
- ^异或运算的性质
- 高效两个变量交换
- 三目运算符(条件运算符)
- 运算符的优先级
- 四种基础类型
- 不同进制的数据赋值
- 浮点类型变量要加后缀f或F
- Java中的浮点值是近似值(不精确)
- switch语句用法
- 取数字的各位
- 整数除法和取余映射
- 循环的分析方式
- for循环极易出现错误的bug地方
- 输出*图形算法
- 输出九九乘法表
- 小整数分解质因数
- 素数判断方法
- 跳转控制语句
- 方法
- 方法重载(重复的意思)
- Java虚拟机中的内存分配模型
- 堆内存与堆
- 本地方法栈举例
- java虚拟机栈内存与堆内存
- 数组
- 数组实质
- 数组变量中存储的内容
- 分析数组两种初始化的内存情况
- 改变数组的内容与数组之间赋值的特殊
- java中的二维数组
- 二维数组典型使用
- 递归
- 运用递归易遇到的异常
- 典型递归问题
Attantion
所有的知识如果只是知识点的堆积,就会很快遗忘。对抗遗忘的方法是自己总结成一张知识网。
记住英文的常用键
横着记,斜着记,或者像中文一样一些字母打多了,就记住了(能打出来不等于记住了键盘的的键位,还是最终要记住键位的位置,才能盲打,而且大字的时候绝对不要一直看键盘,要尝试的打出来)
编程学习的三个阶段
- what 知道怎么用API
- how API怎么实现的,开源框架怎么实现的,怎么改别人的源码
- why 为什么这么设计,系统架构师
java语言的特点
- 简单性 面向对象
- 高性能 分布式处理
- 多线程 健壮性
- 动态 结构中立 安全性
- 开源 跨平台-一次编译,到处运行
当在一个类中找不到明明是该对象中存在的方法时,去它的父类中去找
写代码的好习惯
public class hello{ public static void main(String[] args){ //代码完成的功能一://代码完成的功能二://代码完成的功能三:}}先写整个代码的逻辑,再对各自的模块填充,写代码思路更清楚,代码的说明就顺便写了,不用之后再补。方法写完后再将其中重复使用的或与整个方法的关系不大的代码抽成方法,对于一些常量字符串可以提取成一个字符串
Java程序报错时找错
1.以下输出的是错误栈,看第一条,第一条才是错误最开始的地方
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.company.Fibonacci.printFibonacci(Fibonacci.java:14)
at com.company.Fibonacci.main(Fibonacci.java:10)
java环境变量和软件安装
1.下载jdk(32位还是64位),jdk安装过程中有jdk和jre,这两软件要在java下建立两个文件夹分别放。
2.环境变量配置。(配置的是系统变量,不是用户变量)。电脑右键属性高级系统设置-环境变量。
系统变量里的JAVA_HOME D:\Java\jdk-9.0.1
CLASSPATH .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;(点代表当前目录,;代表分隔)
//这里的点表示在当前目录查找类文件,其中编译和执行Java文件需要dt.jar和tools.jar
path 添加;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
3.检验。win键+r ,输入cmd
输入javac或java有输出
4.某些电脑环境变量配置需要重启电脑,CMD中输入path可以输出当前系统的环境变量判断是否已生效
第一个Java程序
1.notepad建立文件语言java。另存为文件(类名和文件名要一样)
cmd中通过cd语法转到文件。
查询指令cd /?
cd /d f: (转到F盘)
cls 清屏
2.转到后运行 javac new1.java
3.运行java new1
4.结束。
//一个Java源文件可以包含多个类定义,但最多只能包含一个public类定义。
Java中Scanner对象的使用
import java.util.Scanner; // 导入包,java的类的绝对路径表示为:类所在包名+类名
Scanner sc = new Scanner(System.in); //可以连续输入,用空格隔开
int x = sc.nextInt();
int y = sc.nextInt();
System.out.println(x);
System.out.println(y);
Java中System.out.print和System.out.println的区别
public class hello
{ public static void main(String[] args)
{ int n=4;
for (int i=1;i<=n ;i++ )
{ for(int j=1; j<n+i;j++)
{
if(j<=n-i)
{ System.out.print("\t");
}
else {System.out.print("*"+"\t");}//System.out.print()括号部分不能为空,
同时可以使用转义字符注意要引号引起来
}
System.out.println();//注意println()和print()两者的区别
System.out.println()会输出换行
}
}
}
面向对象与基于对象
- 面向对象:有三大封装,继承,多态缺一不可。如java
- 基于对象:没有继承的特点,而多态需要继承的概念,也没有多态的特性,但都有封装。
如Javascript使用封装好的对象,调用对象的方法,设置属性,但无法派生类。
java标识符的规则
由 “A_$8” 组成,且不能有关键字(由小写字母组成)。
数字不能放在第一位。goto和const作为java保留字,java区分大小写class和Class不相同
| 错误标识符 | 正确标识符 |
|---|---|
| 300we (数字开头) | time |
| public (关键字) | aj48 |
| one%n (有%) | _inte_rface |
| Uuwe name (有空格) | $$fji |
标识符的命名规范
类名(接口名):PandaFamily //通常用名词单词首字母大写
方法名:openDoor() //通常用动词,首字母小写,后续首字母大写
变量名:sisterName //首字母小写,后续首字母大写
常量名:GEMDER //全部大写
包名:com.jsf.www //通过域名反转来命名
java注释种类
注释有解释说明和排错的作用
1.// 单行注释
2./*多行注释
多行注释 将文字选中后“ctrl+shift+/ ”可以快速多行注释||多行注释不可嵌套
*/
3./**
*这里是文档注释,javadoc导出注释文档(API文档是文档注释自动生成的)
*/
进制转换
十六进制转十进制 1111 1011 = (8+4+2+1) (8+2+1) = FB(如1111对应位为8+4+2+1,四位一看)
八进制转十进制 111 011 = (4+2+1) (2+1) = 73
原码、反码、补码
//计算机数据都是以补码存贮
原码:带符号位的二进制数
反码:正数相同,负数符号位不变其他位取反 (符号位0表示正)
补码:正数相同,负数符号位取反加1
//补码出现,是由于如果计算机数据都以原码表示则运算时数值位和符号位要单独运算。补码符号位直接参与运算
如:-8 = 1,1000 (符号位常用逗号隔开)
java是强类型语言(对数据类型的要求十分严格)
如c语言中零和非零可以表示真假,Java中不行。if(1){}在c中可以,Java中只可以用true
Java中常量分类
1.字面值常量:
字符串常量 用双引号括起来的内容
整数常量 所有整数 12,23
小数常量 所有小数 12.34,56.78
字符常量 用单引号括起来的内容 ‘a’,’A’,’0’
布尔常量 较为特殊 只有true和false
空常量 null(数组部分讲解)
2.自定义常量(面向对象部分讲):
java数据类型
1.引用数据类型:类(class),接口(interface),数组([])
2.基本数据类型:
byte 1字节 (-2^7)~(2^7-1) //-128~127
short 2字节 (-2^15)~(2^15-1)
int 4字节 (-2^31)~(2^31-1) //int整数默认类型
long 8字节 (-2^63)~(2^63-1)
float 4字节 (-3.403E38)~(3.403E38)
double 8字节 (-1.798E308)~(1.798E308) //double小数默认类型,E是指10的多少次方
char 2字节 -655536-65535 //ASCII表
boolean true/flase //不可以用0和非0表示,也不能直接转化为0和1
3.数据表示表达范围举例:
byte 正数 0-127(即0,1111111) 负数 0~-128(1,0000000) //共八位,其中最高位为符号位
//正数0和负数0表示相同,多出来的负0用来表示-128
如何记忆10000,以及11111的值
1.特值法:10来举例
2.10000:2^4 (次方数0的个数)
11111: 2^5-1 (次方减1,数1的个数)
如何快速计算二进制数
180的二进制:180=128+32+16+4 ,然后与8421对应一样一一对应(想象从高到低128,64,32,16,8,4,2,1有没有数)
最高位为符号位:0,1011 0100
数据类型转换
1.默认转换
byte,short,char—>int—>long—>float—>double //小范围转大范围
byte,short,char相互之间不转换,他们本身和之间参与运算首先转换为int类型再进行运算【非常重要】
2.强制转换
目标类型 变量名=(目标类型)(被转换的数据)
类型转换举例
例1.
byte a = 3; //3是整数常量的默认int类型,编译器”聪明了"计算了3是否在范围内
byte a = 130; //报错,超出范围
byte b1=3,b2=4,b;
b=b1+b2; //报错。是因为b1和b2是变量,变量的值会变化,不确定具体的值,默认使用int类型进行存储,编译器报错
b=3+4; //不报错。3和4都是常量(默认int),java编译时检查常量和是否超出byte类型范围.没有则可以赋值
例2.
System.out.println(‘a’); //输出a
System.out.println(‘a’+1); //输出98,char类型的运算会先转换为int
System.out.println(“hello”+’a’+1);//输出helloa1,从到右的运算顺序,所以是字符串连接
System.out.println(‘a’+1+”hello”);//输出98hello,先计算再链接
例3.
1.强制类型转换非常重要:
从高位往低位依次截断,包括符号位,直到满足位数要求
public class ForceTransfer {
public static void main(String[] args) {
byte a = (byte) 180; //超出byte -128~127的表达范围
byte b = (byte) -140;
System.out.println("a="+a+" "+"b="+b);
}
}
输出结果:
a=-76 b=116
分析:
转换机制
180的二进制:180=128+32+16+4,
对应原码与补码相同:0,1011 0100
高位被截断: 1,011 0100(补码)
转化为原码: 1,100 1011(反码)~ 1,100 1100(原码) = -76
例4.
if(1){}在Java中是不正确的(强类型语言),但在c语言中没错。Boolean不能与int相互转换。
运算符/和%的特殊
1./特殊
int i=1,j=2;
double a =i/j //输出0.0,当参与运算的两个算子都是整数类型,除法结果只保留整数部分
double a =1.0*i/j //输出0.5,因为1.0默认为double
2.%特殊
int k =3%5; 3
int k =(-3)%5; -3
int k =3%(-5); 3 //取余运算的符号取决于被除数(第一个数)
数据溢出
如:
int的最大值+1 = int的最小值
int的最小值-1 = int的最大值
+的两种使用方法
1.+做数值加法
2.字符串的拼接
如:System.out.println(“hello”+’a’+b);
自增运算符
int a =0;
int b =a++; //先使用变量值,再加1 b=0,a=1
int b =++a; //先加1,再使用变量的值 b=1,a=1
特殊+和赋值运算结合
+=,-=,/=,%=,*=先运算再赋值 //本质是赋值运算,如:i+=1等价于i=i+1
&与&&都可以表示逻辑与运算,区别
false&(i+=5); //会判断第二个式子
false&&(i+=5); //第二个式子不会做判断了,第一个false就可以判断,更智能
位运算(直接操作比特位,补码)
1. << >> 带符号位左,右移运算(左右看尖头)
2. <<< >>> 不带符号位左,右移运算
3. 3<<2 = 3*(2^2) = 12
4.带符号位 :符号位不变,正数左右移位添0。负数符号位不变,左移添0,右移添1 //补码
不带符号位:左右移都添0
如:
-4>>2 = -1(int占32位,便于写用byte8位同样)
-4原码:1,000 0100
反码:1,111 1011
补码:1,111 1100
移位:1,111 1111 (补码)
原码:1,000 0001 = -1 (取反加1)
移位特殊情况
int i=2;
System.out.println("i= "+(i<<34)); //java虚拟机做了特殊处理,对int型进行移位运算,移位数=移位数%32(即2)
输出: i=8
^异或运算的性质
1.相同则为0,相异为1
2.a^a = 0; a^0 = a; //连续异或满足交换律和结合律,可利用交换两个变量
高效两个变量交换
1.temp=i;
i=j;
j=temp; //中间变量法
2.a=a+b;
b=a-b;
a=a-b; //加减法
2.a=a+b-(b=a);
3.a=a^b;
b=a^b;
a=a^b; //异或运算的性质a^a = 0, a^0 = a;
三目运算符(条件运算符)
//有运算结果返回时可替代if-else使用
int max = (x>=y)?(x>=z?x:z):(y>=z?y:z)
char a = (x>=100)?'A':(x>=70)?'B':(x>=50)?'C':(x>=30)?'D':'E'
运算符的优先级
正负号 > 单目运算符(++,--,!) > 算数运算符 > 关系运算符(>=...) > 逻辑运算符 > 条件运算符(三目? : ) > 赋值运算
可以通过括号调整运算的优先级
四种基础类型
long 类型使用时加L
public class LongTest{
public static void main(String[] args){
long a = 123456789*87654321;
long b = 123456789L*87654321L;//long类型在使用时要注意加L,否则容易超出long的表示范围
System.out.println(a);
System.out.println(b);
}
}
运行结果:-1276955003
10821521012635269
不同进制的数据赋值
十进制; a=33
八进制; a=033 //以0(零)开头
十六进制;a=0x33 //以0x(零x)开头
浮点类型变量要加后缀f或F
float a = 1.1F; //否则会报错(double可加d,D或不加,因为double是浮点类型的默认类型):
Java中的浮点值是近似值(不精确)
public class DoubleTest {
public static void main(String[] args) {
double a = 4.35*100;
System.out.println(a); //double是浮点类型的默认类型,且浮点值是近似值
System.out.println(Math.round(a)); //四舍五入法Math.round()解决近似值问题
double c = 0.1;
double d = 2.0-1.9;
System.out.println(c);
System.out.println(d);
System.out.println(c==d);
System.out.println(Math.abs(c-d)<1e-6);//使用java提供的去绝对值方法Math.abs()与1e-6比较较小就可以看作相等
}
}
}
运行结果:
434.99999999999994
435
0.1
0.10000000000000009
false
true
switch语句用法
1.switch()中表达式中只能使用byte,short,int,char,string,枚举类型(不能使用boolean,double,float,long类型)
2.case 只能加常量
3.break是跳出整个循环,continue是当前循环。如果case中没有加break,会从匹配的case开始依次执行每个分支代码
(称case穿越,不管是否匹配都执行),此特性可以用来写代码。
4.default分支是否需要加break,取决于它所在的位置,如果是在最后一个分支则不需写,不是最后一个则需写。
例1:
学生成绩打分:
//采用if方法
if (goal >= 90) {
System.out.println("方法一判断 "+'A');
} else {
if (goal >= 60 && goal <= 89) {
System.out.println("方法一判断 "+'B');
} else {
System.out.println("方法一判断 "+'C');
}
}
//采用switch方法
switch ((goal / 10 % 10)) { //此处取百位或之前的位数
case 0:
case 1:
case 2:
case 3:
case 4:
case 5: //此处会若匹配到0或1...会按顺序执行(case穿越)
System.out.println("方法二判断 "+'C');
break;
case 6:
case 7:
case 8:
System.out.println("方法二判断 "+'B');
break;
default:
System.out.println("方法二判断 "+'A');
}
例2:
//switch放字符串时内部是用equal判断但是之前要判空
String operate = getOperate();
if(operate!=null){
switch (operate){
case "register":
break;
case "login":
break;
}
}
取数字的各位
个位:%10
十位:/10%10
百位:/100%10 //都是通过%10的方法演化过来
整数除法和取余映射
整数除法和取余的结果范围是从0开始的
如:1~12月映射到四个季节:
(i-1)/3 ,映射到0,1,2,3
循环的分析方式
求和1+2+3+4+5=?
分析:循环在每次都是将前面部分的和(已累加数据),加上一个数据(当前累加数据)
int sum = 0; //将第一个数据也纳入循环,故写0
for (int i = 1;i<=5;i++){
sum = sum + 1; //连乘同理
}
下面的九九乘法表输出:
按行列输出情况分析
数组数据移动:
数据的前后关系和数组的长度界限
for循环极易出现错误的bug地方
for(int i = 1;i<=5;i++)和int i;for( i = 1;i<=5;i++) //当出现多个循环时int的有效范围易错
如:
输出1到1000的完全数,即除自己之外的所有因子之和等于本身
//若int sum =0定义在这,sum一直会累加
for (int i = 1; i <= 1000; i++) {
int sum = 0;
for (int j = 1; j < i; j++) {
if (i % j == 0) {
sum = sum + j;
}
}
if (i == sum) {
System.out.print(i+" ");
}
}
输出*图形算法
*
**
***
****
分析:看作二维的输出,四行为外循环,内循环为每行输出多少个*
for(int i=0;i<5;i++){
for(int j=0;j<=i;j++){
System.out.print("*") //注意是print
System.out.println() //输出换行符
}
}
输出九九乘法表
分析:行和列和的计数规律
public class MultiplyTable {
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) { //
for (int j = 1; j <= i; j++) {
System.out.print(j+"x"+i+"="+(j*i)+" ");
}
System.out.println(); //这样直接会输出换行
}
}
}
1x1=1
1x2=2 2x2=4
1x3=3 2x3=6 3x3=9
1x4=4 2x4=8 3x4=12 4x4=16
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
小整数分解质因数
将90分解质因数:90=2*(3*3*5)
分析:从最小的质数2开始找起,n%2可以整除故2是它的质因数,如去括号一样再找一个可以整除的质数,
直到最后只剩一个最小质数停止。
for (int i = 2; i < n + 1; i++) {
while (n % i == 0 && n != i) {
n = n / i;
System.out.print(i + "*");
}
if (n == i) {
System.out.println(i);
break;
}
}
//如果这个数很大就是大数分解问题(暂时没有多项式的算法)
算术基本定理:任何一个大于 1 的自然数可以分解成一些素数的乘积;
并且在不计次序的情况下,这种分解方式是唯一的
素数判断方法
用一个数分别去除2到sqrt(这个数),如果能被整除,则表明此数不是素数,反之是素数。
跳转控制语句
continue:跳出当前循环
break : 跳出整个循环(当循环有多层的时候,只会跳出最内层循环)
return :跳出方法/函数 //return本身是用作跳出函数或方法的,它不支持标签
带标签的跳转控制语句:
当有多层循环,需要控制跳出指定的循环时使用。
例1:
Outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 2) {
break Outer; //会跳出标签处的循环
}
System.out.print(" i=" + i + ",j=" + j);
}
System.out.println();
}
例2:
for (int i = 0; i < 3; i++) {
OuterContinue:
for (int j = 0; j < 3; j++) {
if (j == 2) {
continue OuterContinue; //等价于没加标签的break
}
System.out.print(" i=" + i + ",j=" + j);
}
System.out.println();
}
方法
方法就是完成特定功能的代码块(有利于代码复用和代码维护)
使用方法注意:
1.方法不调用不执行
2.方法与方法是平级关系,不能嵌套定义
3.方法定义的时候参数(形参)之间用逗号隔开
4.方法调用的时候不用在传递数据类型
有返回值的方法调用:
1.单独调用,没有意义 // add(3,4);
2.输出调用,有意义,但是不够好,不一定非要把结果输出// System.out.println(add(3,4));
3.赋值调用,推荐方式 //int addResult = add(3,4);
无返回值的方法调用:
1.直接调用。 //但也可以使用return跳出方法
public static void print(String word){
if (word.length()>5){
return ; //无返回值利用return跳出方法
}
System.out.println(word);
}
方法重载(重复的意思)
1.同一个类允许存在一个以上的同名方法,只要它们的(参数个数/参数类型/参数顺序)不一样,方法就不一样
2.虚拟在调用时,通过参数列表的不同来区分同名方法
3.方法名+参数列表=方法签名
4.不能仅由返回值决定调用何种同名方法的原因:
当我们使用赋值调用时,对应的虚拟机有足够的条件(根据调用的返回值类型和要求赋值的类型)去区分该使用
哪个重载的方法。但使用直接调用时,没有返回值,虚拟机无法判断。
short addResult = add(x,y);
addResult = add(a,y);
5.当实参的类型与形参不同是时,会匹配可以类型转换的方法
如: int a=4;
byte b=5;
equals(a,b);
public static boolean equals(int a,int b){ }; //byte可以默认转化为int,会调用该方法
public static boolean equals(byte a,byte b){ };
Java虚拟机中的内存分配模型
1.栈内存 存储局部变量
2.堆内存 存储new出来的东西 (与数据结构堆无关,下面有解释)
3.方法区 //储存的是class字节码文件(java编译后的内容)、方法(在方法区中的方法区域中,且存的是java虚拟机的指令,它类似于汇编语言的指令)、静态变量和静态方法(static 修饰,方法区的静态区)、常量(方法区的常量池中,存字面型常量和final修饰的变量)
4.本地方法栈 (系统相关) //(JNI,java语言用来调用其它语言提供的功能)
5.程序计数器 //是当前线程所执行的字节码(java编译出的.class文件)行号的指示器
堆内存与堆
1.最开始的堆内存中使用了“堆”这种数据结构。
据说,堆数据结构在1960年代中期就已经提出来了。而堆内存在1970年代初期才实现。
2.堆只是一个普通的词。在堆内存和堆数据结构中分别使用了。但堆内存并没有使用堆数据结构(使用的是类似链表的结构)。
堆内存,表示“一堆”可以分配出去的内存。堆内存的含义是指内存分段
本地方法栈举例
String类举例
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
.....
public native String intern(); //这句 native 是使用了java语言使用了其他语言。
}
java虚拟机栈内存与堆内存
栈内存:
1.存储局部变量(方法中定义的变量)
2.必须显示初始化,没有默认值 (对比数组动态初始化时,系统有默认值,不同类型有不同默认值)
3.局部变量一旦超出作用域,立马会被虚拟机回收
堆内存:
1.存储的都是new出来的东西
2.所有new 出来的东西,系统都会进行默认初始化
3.堆内存中new 默认初始化的值:
byte,int,short 0
char '\u0000' (\u开头是一个Unicode码字符,每一个'\u0000'都代表了一个空格)
float,double 0.0
boolean false
引用类型,String null //引用类型(类(class),接口(interface),数组([]))
4.堆内存中,即使某一块内存已经成为垃圾,也不会被立即回收,堆内存的回收是由垃圾回收器完成的(垃圾回收器空闲时才回收)
数组
1.数组中可以存基本数据类型,引用数据类型引(类(class),接口(interface),数组([]))
2.数组定义两种方式:
int[] array ;
int arry[]; 【重要,这种形式在笔试中易出现】//如mian方法的形参也可以是 main(String args[])与main(String[] args)等价
3.数组的两种初始化分析:
动态初始化:(只指定数组长度,由系统为数组分配初始值)
int[] array = new int[10];
静态初始化:
int[] array = new int[]{5,4,6,6};
int[] array = {5,4,6,6} //简洁版
数组实质
(实质是引用变量,类似于指针中引用,数组是java中的内置对象)
1.对于数组空间我们只能通过,引用变量(栈内存中)来间接操作数组在堆内存上对应的内存空间
2.引用变量与它指向的堆上的内存空间之间的关系,很像遥控器和电视机的关系
3.当堆内存中的内容,没有任何引用变量引用它,它就变成了垃圾
数组变量中存储的内容
int[] array = new int[10];
System.out.println(array)
traverse(array)
输出结果:
"[I@14ae5a5" //”[“代表是一维数组,”[[“代表是二维数组,I代表数组的元素是整型,@后面跟的由数组首地址经过hash函数转换过的数值
数组元素是:
0 0 0 0 0 0 0 0 0 0 //数组未初始化时装的是默认值,int[]默认值是0
分析数组两种初始化的内存情况
1. int[] array = new int[10]; //动态初始化
首先:在堆内存中创建|0|0|0|0|0|0|0|0|0|0|,一个连续空间,并赋予默认值0,它的首地址为OX10110
其次:在栈内存中创建int[]类型的变量,变量名为array
最后:将堆内存中的首地址(经过转换),赋值给array
2. int [] array = new int[]{5,4,6,6}; //静态初始化
int [] array = {5,4,6,6} //简洁版
首先:在堆内存中创建|0|0|0|0|,一个连续空间,并赋予默认值0,后将元素重新赋值|5|4|6|6|,它的首地址为OX10110
其次:在栈内存中创建int[]类型的变量,变量名为array
最后:将堆内存中的首地址(经过转换),赋值给array
改变数组的内容与数组之间赋值的特殊
/*
定义两个数组,先定义一个数组,赋值,输出。
然后定义第二个数组的时候把第一个数组的地址赋值给第二个数组。
然后给第二个数组赋值,再次输出两个数组的名及元素。
*/
import java.util.Random;
public class ArrayChange {
public static void main(String[] args) {
int [] array1 = new int[10];
traverse(array1);
changeArrayValue(array1); //通过传数组名,方法内改变数组元素可以改变对应数组元素
traverse(array1);
int [] array2 = new int[10];
array2=array1; //array2=array1指的是将array1对应的地址传给array2
traverse(array2); //所以此时输出的array2就是array1
changeArrayValue(array1); //改变array1,而array2指向的array1,所以输出的array2是改变后的array1
traverse(array2);
}
public static void traverse(int [] array){
System.out.println("数组元素是:");
for (int i = 0; i <array.length; i++) {
System.out.print(array[i]+"\t"); //"\t"和“ ”一样都是输出空格,但"\t"可以自动对齐
}
System.out.println(); //打印完加个换行符
}
public static void changeArrayValue(int [] array){
for (int i = 0; i <array.length; i++) {
array[i]= new Random().nextInt(100); //输出1-100的随机数
}
}
}
输出:
数组元素是:
0 0 0 0 0 0 0 0 0 0
数组元素是:
61 85 51 85 25 1 99 52 19 94
数组元素是:
61 85 51 85 25 1 99 52 19 94
数组元素是:
67 1 1 63 41 24 78 69 32 66
数组中常考察的典型算法:
数组常见异常:
1.数组越界:
java.lang.ArrayIndexOutOfBoundsException:
int[] array = new int[10];
System.out.println(array[10]); //没有array[10],只有到array[9]
2.空指针异常:
java.lang.NullPointerException:
int[] array = new int[10];
array = null;
System.out.println(array[0]); //空指针异常
java中的二维数组
(重要,可以不止存放矩阵,可以自己定义不同的“每行的长度”,就可以存类似三角形,节省空间)
1.格式一:
int [][] array = new int [2][3];
System.out.println(array); //输出[[I@4554617c,”[[“代表是二维数组,I代表数组的元素是整型,@后面跟的由数组首地址经过hash函数转换过的数值
首先:在堆内存中创建|0|0|,一个连续的空间,它的首地址为OX10110。创建|0|0|地址为oxabc,
创建|0|0|0|地址为oxafbb。将地址oxabc和oxafbb赋值给OX10110对应的空间|oxabc|oxafbb|
其次:在栈内存中创建int[][]类型的变量,变量名为array
最后:将堆内存中的首地址OX10110(经过转换),赋值给array
2.格式二:
int [][] array = new int[2][];
此时array[0]和array[1]中是null,引用类型
array[0] = new int[1];
array[1] = new int[2]; //行长
3.格式三:
int[][] array = new int[][]{{3, 4, 5}, {6, 7, 8}, {9, 10, 11}};
int[][] array = {{3, 4, 5}, {6, 7, 8}, {9, 10, 11}}; //简洁版
二维数组典型使用
递归
//方法定义中调用方法本身。
核心思想:分治,大规模问题转换为与原问题结构相似的子问题,然后再将解决问题的过程总结为
1.要有出口,否则是死递归
2.调用次数不能太多,否则内存溢出
3.构造方法中,不能使用递归
运用递归易遇到的异常
java.lang.stackoverflowerror
1.栈帧:在方法运行时(方法被调用时),需要给各个方法在栈内存中分配相应的存储空间,方法被调用一次就会在栈内存上
为其分配一片存储空间用来存储,该方法本次运行所需要的内存空间
//构造方法与普通的方法执行不同,是一种特殊的方法,它不会在栈中创建栈帧
2.方法调用时创建栈帧,只有到方法结束时,才会销毁栈帧,并回收相应的内存空间
3.java.lang.stackoverflowerror,当栈空间被填满,我们继续向栈内存中申请内存空间此时会报这个错误
典型递归问题
1.递归计算n!
public class Factorial {
public static int fun(int n){
if(n==0||n==1){
return 1;
}
return fun(n-1)*n;
}
public static void main(String[] args) {
System.out.println(fun(4));
}
}
2.汉诺塔问题:
3.走阶梯问题:
/* 一个楼梯有n级,每次走1级或两级,请问从1级台阶走到第n级台阶一共有多少种走法
* 设N级台阶有f(n)种走法 f(1)=1,f(2)=2,f(3)=4 到第N阶,考虑最后一步,有1,2,3级三种登法
* 所以f(n)=f(n-1)+f(n-2)+f(n-3) 所以可以用递推公式推到第N项
* */
public class WalkMathod {
public static void main(String[] args) {
System.out.println(walkMathod(6));
}
private static int walkMathod(int n) {
if(n==1){
return 1;
}
if(n==2){
return 2;
}
if(n==3){
return 4;
} //递归出口
return walkMathod(n-1)+walkMathod(n-2)+walkMathod(n-3); //最后一个大问题,转化为小问题的解决,再加上某种操作,再回溯
}
}
4.斐波那契数列问题:
/*利用递归实现菲波那切数列的计算
* F0=0,F1=1,Fn=Fn-1+Fn-2
* 输出斐波那契数列
* */
public class Fibonacci {
public static void main(String[] args) {
printFibonacci(9);
}
public static int[] printFibonacci(int n){ //打印出几位斐波那契数列
int [] array = new int [n];
for (int i= 0;i<n;i++){
array[i]=fibonacci(i);
}
traverse(array);
return array;
}
public static int fibonacci(int n){
if (n==0){
return 0;
}
if (n==1){
return 1; //递归出口
}
return fibonacci(n-1)+fibonacci(n-2); //斐波那契数列的递推公式
}
private static void traverse(int [] array){
System.out.println("斐波巴切数列:");
for (int i = 0; i <array.length; i++) {
System.out.print(array[i]+" ");
}
System.out.println();
}
}
