JDK:Java 开发工具包,包含JRE、JVM,是开发环境
JRE:Java 运行时环境,包含JVM
JVM:Java 虚拟机,用来解释class文件
输出语句System.out.println(12456); System.out.println('A'); System.out.println("A"); System.out.println("Hello Java"); System.out.println("");声明变量初始化
//声明 int i; //初始化 i = 520; System.out.println(i); //初始化 i = 1314; System.out.println(i); //声明 int i2; //初始化 i2 = 666; System.out.println(i2); System.out.println(""); //-128~127,超出会报错 byte b = 127; System.out.println(b); short s = 2; System.out.println(s); int i3 = 3; System.out.println(i3); //后面数字默认是int类型,当数字超过int类型我们需要在后面加上long类型然后 赋给变量。 long l = 12345678912L; System.out.println(l); //小数默认double,所以小数直接赋值给float会报错,需要在数字后面加F。 float f = 1.2f; System.out.println(f); double d = 20.1; System.out.println(d); char ch = 'a'; System.out.println(ch); char ch1 = 90; System.out.println(ch1); //在单引号之内,一个单位才算字符,这里有两个字符,所以报错。 char ch2 = '65'; char ch3 = '张'; System.out.println((int)ch3); char ch4 = '无; System.out.println((int)ch4); char ch5 = '忌'; System.out.println((int)ch5); boolean flag = true; System.out.println(flag);强制转换
b = (byte)s; b = (byte)ch; System.out.println(b); s = b; s = (short)ch; i = b; i = s; i = ch; i = (int)l; i = (int)f; i = (int)d; l = b; l = s; l = i; l = (long)f; l = (long)d; l = ch; ch = (char)l; ch = (char)b; ch = (char)i; ch = (char)s; ch = (char)f; ch = (char)d; i = 129; //数据溢出 b = (byte)i; System.out.println(b);数据类型自动转换的优先顺序
byte——>short——>char——>int——>long——>float——>double
注意:两个char型运算时,自动转换为int型,当char与别的类型运算时,先转换成int型,再做其它类型的自动转换
1:不能对boolean类型进行类型转化
2:不能把对象类型转换成不相关类的对象
3:在把容量大的转化成容量小的类型时必须使用强制类型转换
4:转换过程中可能导致溢出或损失精度
5:浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入
数据类型 常量 变量的重点在于 变,指在程序运行过程中值可以不断的变化
常量的重点在于 常,指在程序运行过程中不能发生改变
被final修饰的变量就叫做常量,常量只能赋值一次,一般存储不会改变的值
final int AGE; AGE = 18; System.out.println(AGE);算术运算符:+,-,*,/,%
int i = 100; System.out.println(i+50); System.out.println(i-50); System.out.println(i*50); System.out.println(i/50); System.out.println(i%50);赋值运算符:=,+=,-=,*=,/=,%=,++,–
指的是运算之后重新赋值给变量,即使用运算符会改变变量的值
赋值运算符都会自动进行隐式强制转换
++i是指先加后用,i++是指先用后加
i+=50; System.out.println(i); byte b= 5; b = (byte) (b + 5); //隐式强制转换 b+=5; i = 100; System.out.println(i++); System.out.println(++i);字符串连接符:+
字符串不能进行任何运算,只能使用+进行连接,则任何数据碰到字符串都会变成字符串
代码是从左往右,从上到下运行的。+的左右是数字就进行加法运算,如果一侧有字符串就变成字符串
System.out.println(5+5+"Hello"+5+5);优先运算符:()
改变同一行代码中的执行顺序
System.out.println(5+5+"Hello"+(5+5));比较运算符:==,<,<=,>,>=,!=
通过代码执行,可以发现代码执行后得到的返回值都是布尔值
称所有比较运算为:布尔表达式,因为比较运算的运算结果是布尔值,通过他来给布尔变量赋值
重点:字符串以及所有的引用数据类型不能使用进行对比,因为引用数据类型使用比较的是内存地址,所以可以使用对象名.equals()方法来进行比较,equals方法可以比较引用数据类型中具体的值是否一致。
System.out.println(i == 100); System.out.println(i > 100); boolean boo = i < 100; System.out.println(boo);String
类型比较特殊,在创建字符串变量的时候,可以使用类似基本数据类型的方式创建
String str = "Hello";
也可以使用String构造方法来创建字符串变量
String str2 = new String("Hello"); String str3 = "Hello"; String str4 = new String("Hello"); System.out.println(str); System.out.println(str2); System.out.println(str == str2); System.out.println(str == str3); System.out.println(str == str4); System.out.println(str.equals(str2)); System.out.println(str.equals(str3)); System.out.println(str.equals(str4));逻辑运算符:&&,&,||,|,! 主要用来拼接多个比较运算符 &&,&所有的条件全部返回true时,结果为true;否则,结果为false
&&短路与:当左侧条件结果为false的时候,会进行短路直接返回false,不执行右侧条件
System.out.println(!(i == 100 && (i+=200) > 100)); System.out.println(i);
&位与:必须执行完所有条件,才会返回结果,位与可以进行位运算
System.out.println(i == 100 & (i+=200) > 100); System.out.println(i);||,|所有条件中只需要一个条件返回true时,结果为true;否则,结果为false
||短路或:当左侧条件为True的时候,会进行短路返回true
i = 101; System.out.println(i > 100 || (i+=100)>50); System.out.println(i);
|位或:必须执行完所有的条件才会返回结果,并且位或可以进行二进制运算
System.out.println((i+=200) > 100 | (i+=100)>50); System.out.println(i);!对判断结果进行取反 *** 作 三目运算符(三元)
可以在不同情况下来给变量副不同的值,true的时候是一种值(左边),false的时候是另一种值(右边)
int i = 59; int number = i >100? i:100; System.out.println(number);选择控制流程语句之if Scanner扫描流
这个类的对象可以扫描控制台,实现从控制台反向向程序代码中输入内容
Scanner类中所有方法都是属于对象的,所以需要先获取Scanner对象才能使用里面的方法
注意:(1)、Scanner这个类不在我们自己的包下,所以使用它时需要导包 使用java.lang包下的类,以及自己包下的类不需要导包,其余都要导包
(2)、在使用Scanner扫描流的时候,建议在输入之前给出一句提醒,必须要输入内容
Scanner sc = new Scanner(System.in); // 从控制台输入一个字符串 String name = sc.next(); // 从控制台输入一个整数 int age = sc.nextInt(); // 从控制台输入一个小数 double d = sc.nextDouble(); System.out.println(name + " " + age + " " + d);Math方法的使用
是Java提供给我们的数学 *** 作类,里面有很多数字 *** 作类方法,这里主要用它来生成随机数
Math方法中很多属于类,这些方法可以使用类名称,方法名直接调用
Math.random():会随机产生一个处于0~1之间的随机小数,最小(大)无限接近0(1)
Math.random()*10:随机数范围变成0~10 (int)(Math.random()*10):变成0~9的随机整数
(int)(Math.random()*10+1):变成1~10的随机整数
System.out.println(Math.random() * 10); // 0~9 System.out.println((int) (Math.random() * 10)); // 0~10 System.out.println((int) (Math.random() * 10 + 1));If的使用
是Java中的选择流程控制语句,可以让我们在不同的情况下执行不同的代码
如果使用多个if,所有的if之间没有关系,可能会同时执行多个if,这时就需要将所有的if连成一个整体,这样就只执行一个if
使用else关键字,连接所有的if条件,让其变成一个整体 if else支持嵌套
System.out.println("请输入年龄:"); int age = sc.nextInt(); double price = 100; if (age > 80) { System.out.println("您是80以上的老人可以享受免费!!!"); } else if (age > 60) { price *= 0.5; System.out.println("60以上80以下老人票价为:" + price + "元"); } else if (age < 18) { price *= 0.8; System.out.println("未成年票价为:" + price + "元"); } else if (age >= 18) { System.out.println("成年人全价:" + price + "元"); }
单独配合if使用时,如果if的条件为false,则执行else的代码
if语句中else单独使用是指除了if条件以外所有的情况都执行
if (age < 18) { System.out.println("未成年"); } else { System.out.println("成年"); }案例一:输入账号密码,并使用If语句判断对错
System.out.println("请输入您的账号:"); String username1 = sc.next(); if (username1.equals("admin")) { System.out.println("请输入您的密码:"); String password1 = sc.next(); if (password1.equals("6666")) { System.out.println("输入正确"); } else { System.out.println("您输入的密码有误"); } } else { System.out.println("您输入的账号有误"); }案例二:使用If语句判断年份是平年还是闰年 第一种方法
Scanner sc = new Scanner(System.in); System.out.println("请输入年份:"); int year = sc.nextInt(); if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) { System.out.println("闰年"); } else { System.out.println("平年"); }第二种方法
Scanner sc = new Scanner(System.in); System.out.println("请输入年份:"); int year = sc.nextInt(); if (year % 400 == 0) { System.out.println("闰年"); } else if (year % 4 == 0 && year % 100 != 0) { System.out.println("闰年"); } else { System.out.println("平年"); }switch
java控制语句之一,但与if不同,If可以使用范围判断
switch不支持范围判断,只支持具体的数值的判断,所以在可用性上比if差很多,一般只有选择的可能性较少的时候才使用swith
switch的语法:
switch(key){
case value:
break;
default:
break;
}
其中key需要放变量或具体的值,case后面的value不支持布尔表达式,只能放具体值
如果后面的值于key相同,则会执行case中的代码,所有的value值不能重复
如果所有的case都没匹配成功,则会执行default的代码
break:java关键字之一,作用为打断当前作用域的代码
1:switch支持byte、short、int、char、String、枚举,不支持long、float、double、boolean
2:switch括号中可以放变量以及符合要求的值,case后面只能放值
3:switch在case对比成功后,执行后面所有的代码,不再进行比较,哪怕之后case不符合要求,也会执行case里面的代码,这种特性叫switch的穿透性,所以我们需要在每个case代码的最后使用break关键字,避免执行case中代码
4:当所有case对比失败,会进入default代码,而default是可要可不要的
Scanner sc = new Scanner(System.in); System.out.print("请输入你的数字:"); int i = sc.nextInt(); switch(i){ case 1: System.out.println("1"); break; case 2: System.out.println("2"); break; default: System.out.println("其他"); }案例一:使用switch判断年龄20岁以下为少年,40岁以下为青年,60以下为中年,其余为老年
Scanner sc = new Scanner(System.in); System.out.print("请输入您的年龄:"); int age = sc.nextInt(); switch(age/20){ case 0: System.out.println("少年"); break; case 1: System.out.println("青年"); break; case 2: System.out.println("中年"); break; default: System.out.println("老年"); }案例二:判断平年还是闰年
System.out.println("请输入年份:"); int year = sc.nextInt(); switch(year%400){ case 0: System.out.println("闰年"); break; default: switch(year%4){ case 0: switch(year%100){ case 0: System.out.println("平年"); break; default: System.out.println("闰年"); } break; default: System.out.println("平年"); } } year = (year%100 == 0 || (year%4 ==0 && year%100 !=0))? 0:1; switch(year){ case 0: case 1: System.out.println("闰年"); break; default: System.out.println("平年"); }案例三:实现简单的剪子石头布,要求输出人机与玩家出了什么,并判断输赢
System.out.println("请玩家输入 0:石头1:剪刀2:布"); int command = sc.nextInt(); int machine = (int)(Math.random()*3); String str= ""; switch(command){ case 0: System.out.println("玩家出石头"); switch(machine){ case 0: System.out.println("人机出石头"); str = "平局"; break; case 1: System.out.println("人机出剪刀"); str = "你赢了"; break; case 2: System.out.println("人机出布"); str = "你输了"; break; } System.out.println(str); break; case 1: System.out.println("玩家出剪刀"); switch(machine){ case 0: System.out.println("人机出石头"); str = "你输了"; break; case 1: System.out.println("人机出剪刀"); str = "平局"; break; case 2: System.out.println("人机出布"); str = "你赢了"; break; } System.out.println(str); break; case 2: System.out.println("玩家出布"); switch(machine){ case 0: System.out.println("人机出石头"); str = "你赢了"; break; case 1: System.out.println("人机出剪刀"); str = "你输了"; break; case 2: System.out.println("人机出布"); str = "平局"; break; } System.out.println(str); break; default: System.out.println("玩家输入错误,游戏结束"); }Java中转义字符
System.out.println(123); 在输出内容之后,进行换行 *** 作
System.out.println(123); 在输出内容之后,不进行换行 *** 作
err:红色的输出语句,err与out是两个线程,一般不建议两个一块使用,因为不同的线程执行的都是各自的顺序,数据可能会乱
System.out.printf() 在输出语句中可以使用占位符
" 输出一个双引号
’ 输出一个单引号
n 代表换行
t 代表一个tab的距离
\ 输出一个
\\ 输出\
System.out.println(123); System.err.println(123); System.err.print(123); System.out.println(123); System.out.printf("%d %d",12,13); System.out.print("n""); System.out.println("'");For循环
无论生活中,还是程序中循环都是必不可免的,在生活中戴阳每天日出日落,开车轮胎转动,打印机复印文件等;
以打印机为例:
小明复印100张身份z:1、循环内容是复印身份z 2、循环次数是100次
从上面例子中可以发现,循环需要有循环的内容,以及循环的开始是需要一定的条件的,循环一般有次数
Java中最常见的循环是for循环,for循环语法如下:
1 2 4
for(声明初始化变量;判断循环条件;改变变量的初始值){
循环的内容; 3
}
1、声明并初始化变量的 *** 作,在循环中只执行一次
2、每次循环之前,条件为true才会执行,如果为false循环结束
3、每次循环是循环体内的代码都会执行
4、当循环结束后,会自动执行第四部分,改变变量的初始值
5、for循环中1、2、4部分都可以不写,但必须要存在两个分号,也可以只写一部分
6、声明的变量不一定非要是int,以及最后改变初始值不一定非要++,只要改变变量的值就可以了
7、for循环支持嵌套
for(int i = 0;i<10;i+=2){ System.out.println(i); } int m = 0; for(;m<10;){ System.out.println(m++); } for(;;){ System.out.println(123); }break与continue的区别
break:打断当前作用域的代码,破坏掉循环结构,循环中我们可以通过标记来指定,让break打断指定作用域的代码,表示方式是使用x来标记
continue:跳出本次循环,进行下一次循环
for(int i=0;i<5;i++){ System.out.println(i+"外循环-------"); for(int j=0;j<5;j++){ if(j==2){ break; } System.out.println(j+"内循环------"); } } for(int i=0;i<5;i++){ System.out.println(i+"外循环-------"); for(int j=0;j<5;j++){ if(j==2){ continue; } System.out.println(j+"内循环------"); } }增强for循环
使用增强for循环遍历数组:增强for循环可以在每次循环的时候,都将数组中的值取出来赋值给前面的变量,要求前面声明的变量类型必须要与数组的类型一致
需要注意的是,数组赋值是通过下标 *** 作,而增强for循环中数组是没有下标的,所以不能使用增强for循环给数组赋值
只能使用增强for循环读取数组的值
int[] arr = new int[10]; for (int i = 0; i < arr.length; i++) { arr[i] = (int)(Math.random()*20); }案例一:在控制台输出:1+2+3+…+100=5050
int sum = 0; for(int i=1;i<101;i++){ sum +=i; System.out.print(i+(i==100? "=":"+")); if(i<100){ System.out.print(i+"+"); }else{ System.out.print(i+"="); } } System.out.println(sum);案例二:计算0~100之间偶数之和
int sum1 = 0; for(int i = 0;i<101;i += 2){ sum1 += i; } System.out.println(sum1);案例三:打印所有3的倍数以及包含3的数,并计算他们的和,一直到和超过1000为止
int sum2 = 0; for(int i = 0;sum2 < 1000;i++){ if((i%3 ==0) || (i % 10 == 3) || (i%100== 3) || (i/10%10 == 3)){ System.out.println(i); sum2 += i; } } System.out.println(sum2);案例四:输出所有水仙花数(水仙花数是三位数,各个位数的立方和等于本身)
int g,s,b; for(int i = 100;i<1000;i++){ g = i%10; s = i/10%10; b = i/100; if(g*g*g+s*s*s+b*b*b == i){ System.out.println(i); } }案例五:乘法口诀表
for(int i = 1;i<10;i++){ for (int j = 1; j <= i; j++) { System.out.print(j+"*"+i+"="+j*i+"t"); } System.out.println(); }案例六:从控制台输入数字,输入的是几就生成多高的菱形,注意一定是奇数
Scanner sc = new Scanner(System.in); System.out.println("请输入你的数字:"); int n = sc.nextInt(); if(n%2 != 0){ for (int i = 1; i <= n/2+1; i++) { for(int j = 1;j0;i--){ for(int j=n/2;j>=i;j--){ System.out.print(" "); } for(int j = 0;j while循环 while循环实际上就是一个拆分的for循环,在while循环的小括号中,省略了for循环的第一步和第四步
只保留了判断循环的条件,只有当条件为true才会执行循环,如果为false则不执行 while的执行就分两步
1:判断循环条件是否成立
2:执行循环体
while循环适用于,不明确循环次数时,可以使用while循环,一般经常使用while去遍历结果集,例如通过字节读取一个文件,或者解析从数据库获取结果集
while循环与for循环一样都有可能不执行,因为他们都是先判断循环条件,再决定是否循环int i=0; while(i++ < 10){ System.out.println(i); } do{
循环代码体
}while(布尔表达式);
do while由于循环体在while上面,所有会先执行一次循环,然后去判断循环条件,如果为true则继续执行,如果为false则停止执行,由于顺序问题,代表着do while循环无论怎样都会执行一次i=1; do{ System.out.println("---------"); i++; }while(i<10);数组 变量可以用来存储数据,但是变量有一个缺点,一个变量在同一时间只能存储一个值,如果我们代码中需要同时存储很多数据的时候,使用变量变得相当麻烦,例如:此时我们需要存储100个学生的成绩,那么我们就需要定义100个变量来实现,代码就会变得相当复杂,此时数组就可以解决这个问题。
数组:
1:如果我们将变量看成一个单独的调料瓶,那么数组就可以看成是一组调料瓶,他可以实现同时存储多个数据。
2:数组可以实现同时存储多个数据,并且通过下标来 *** 控里面的值,需要注意的是,数组的第一次初始化的时候就已经指定
3:需要注意的是,数组的长度在第一次初始化的时候就已经指定,不能更改
元素:是指数组中保存的数据,如果没有对数组里面的元素赋值的话,里面的元素都是类型的默认值
默认值:整型默认0,浮点型0.0,布尔默认false,引用类型默认null
长度:是指数组中可以保存的元素个数,长度可以通过数组名.length属性获取长度
下标:表示的是数组中元素的位置,我们可以通过数组名[下标]来 *** 控数组中的值,下标从0开始永远比长度-1,也就是说下标永远小于长度ArrayIndexOutOfBoundsException下标越界异常
数组的创建分两步:
1:声明 类型名 对象名[]; 例:int[] array;
2:初始化
2.1:静态初始化是指在初始化数组的时候,直接给数组里面的元素赋值
2.2:动态初始化是指在初始化数组的时候指定长度,不进行赋值
数组使用起来并不复杂,可以跟变量一样去使用,只是多了一个下标的指令int[] array = {1,2,4,5,7}; System.out.println(array.length); int[] array1 = new int[10]; System.out.println(array1.length); System.out.println(array[4]); // 由于数组可能会很长,所以我们通过一个一个下标去查看会很麻烦,所以使用 for循环遍历数组,以及对数组的赋值,总结数组的下标从0开始,永远比数组的 length小,所以可以使用for循环计数器,来充当下标来 *** 控数组 for(int i = 0;i 案例一:使用数组循环录入5名学生成绩信息,然后升序排序输出成绩int[] scores = new int[5]; Scanner sc = new Scanner(System.in); for (int i = 0; i < scores.length; i++) { System.out.print("请输入第"+(i+1)+"名同学成绩:"); scores[i] = sc.nextInt(); } for (int i = 0; i < scores.length; i++) { for (int j = 0; j < scores.length-i-1; j++) { if(scores[j]>scores[j+1]){ int temp = scores[j]; scores[j]=scores[j+1]; scores[j+1] = temp; } } } for(int i:scores){ System.out.println(i); }Arrays工具类 Arrays:JRE提供的一个工具类,专门用于 *** 作数组,里面有很多关于数组的方法
Arrays.toString(array): 可以将数组转化成字符串
Arrays.sort(array); 将数组的元素进行升序排序
Arrays.binarySearch(array,13) 返回元素在数组中的下标位置,如果是负数代表不存在,二分查找,需要有序
Arrays.fill(array, 555) 将数组中的元素全部变成555
Arrays.copyOf(array, 10) 复制一个长度为10的数组,并且在有效长度之内新数组的值与老数组的值相同,超出的部分为0
Arrays.copyOfRange(array, 2, 10) 从下标2开始拷贝,到下标10截止,将内容复制到新数组中,长度不够用默认值补。
Arrays.equals(copyRange, copyRange1)对比数组中的值是否相等。int array[] = {54,53,54,59,2,646,89,546}; //直接输出是一个内存hash地址 System.out.println(array); System.out.println(Arrays.toString(array)); Arrays.sort(array); System.out.println(Arrays.toString(array)); System.out.println(Arrays.binarySearch(array,13)); Arrays.fill(array, 555); System.out.println(Arrays.toString(array)); int[] copyOf = Arrays.copyOf(array, 10); System.out.println(Arrays.toString(copyOf)); int[] copyRange = Arrays.copyOfRange(array, 2, 10); int[] copyRange1 = Arrays.copyOfRange(array, 2, 10); System.out.println(Arrays.toString(copyRange)); System.out.println(Arrays.equals(copyRange, copyRange1)); System.out.println("***************************"); //字符串 String[] ss = {"b","a","c","d"}; System.out.println(ss); System.out.println(Arrays.toString(ss)); Arrays.sort(ss); System.out.println(Arrays.toString(ss)); System.out.println(Arrays.binarySearch(ss, "a")); String[] s1 = Arrays.copyOf(ss, 20); System.out.println(Arrays.toString(s1)); String[] s2 = Arrays.copyOfRange(ss, 0, 3); System.out.println(Arrays.toString(s2)); String[] s3 = Arrays.copyOfRange(ss, 0, 3); System.out.println(Arrays.equals(s2, s3));数组中的算法 冒泡排序:冒泡排序是指,每次都是相邻的两个元素相比较,如果大于或小于对方则互换位置int[] arr = {5,78,2,742,8}; for (int j = 0; j < arr.length-1; j++) { //防止数组越界 for (int i = 0; i < arr.length-1-j; i++) { //左边比右边的元素小 if(arr[i] 选择排序:每次循环都选取出最大或最小的下标,然后将其里面的元素放入到有序数组的最末位;简单理解,每次循环找出最大值(最小值),然后将其放入到数组的头部开始排序for (int i = 0; i < arr.length - 1; i++) { //刚开始i就是最大或最小值的下标 int index = i; for (int j = i + 1; j < arr.length; j++) { if (arr[j] < arr[index]) { index = j; } } //index不等于i代表i当前的元素不是最小值,所以需要换值 if(index != i){ int temp = arr[index]; arr[index] = arr[i]; arr[i] = temp; } } System.out.println(Arrays.toString(arr));获取生成验证码 方法一String s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String code= ""; for(int i = 0;i<6;i++){ int number = (int)(Math.random()*s.length()); code += s.charAt(number); } System.out.println(code);方法二char[] ch = new char[6]; for(int i = 0;i内存划分57&&number <=83){ ch[i] = (char)(number+7); }else{ ch[i] = (char)(number+13); } } String s1 = new String(ch); System.out.println(s1); 1:程序计数器:一块较小的内存,是当前线程执行的字节码的行号指示器,每个线程都有一个独立的程序计数器,并且互不影响。
2:Java虚拟机栈:线程是私有的,其生命周期与线程相同,其主要目的就是描述Java方法执行的内存模型。每个方法在执行时都会创建一个栈帧,这个栈帧存储了局部变量, *** 作数, *** 作数栈,动态链接,方法出口等一些信息,每一个方法从调用到执行完成这个过程对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表:存放着编译期可以知道的各种基本数据类型,对象的引用。
3:本地方法栈:
4:Java堆:虚拟机中最大的一块内存,堆内存被所有的线程共享,堆内存唯一的功能就是存放对象的实例,几乎所有的对象都在这里分配内存。堆内存是垃圾收集管理器管理的主要区域所以也叫GC堆。
5:方法区:与堆一样被所有线程共享,方法区主要存储的是被虚拟机加载的所有类型信息,常量,静态变量,接口,字段,方法,常量池,域信息,方法信息等内容。
常量池:字面量,符号引用
符号引用:1:类的全限定名(包名.类名) 2:字段名和属性 3:方法名和属性
类型信息:1:这个类型的全限定名 2:其直接父类的全限定名 3:类的修饰符(访问修饰符,是否抽象,是否final等) 4:这个类型直接接口的有序列表
域信息:1:成员变量的访问修饰符 2:成员变量是否静态 3:成员变量是否final 4:成员变量是否volatile(用于多线程的一个关键字) 5:成员变量是否transient(用于序列化的一个关键字) 6:成员变量的类型 7:成员变量的名称
方法信息:1、方法的访问修饰符 2、方法是否abstract或native 3、方法是否静态,是否final,是否synchronized(是否同步,多线程的一个关键字)等 4、方法的返回值类型
5、方法名称 6、方法的参数个数和类型 7、异常表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NWE3MYM-1637668000682)(C:UsersAdministratorDesktop栈帧.png)]
类(属性(变量)、方法、代码块)和对象 类和对象的关系 类是对象的抽象,对象是类的具体实例。解释:是一个抽象的概念,没办法指着类说它具体是谁,因为类指向的是一个大体,类里面抽离了所有实例对象共同特征形成属性和行为,在类中这些属性没有具体的值,因为这些属性虽然写在类里面,但是属性具体的值却是属于实例化出来的对象。
属性(变量) 全局变量 1:直接声明在类中,其作用域是整个类
2:全局变量有默认初始值(不需要初始化也可以直接使用)
全局变量两种修饰方法 1:被static修饰的变量,存储在常量池中,被所有对象共享,可以通过类名直接调用
2:没有static修饰的变量,存储在堆内存中,只能通过对象名调用,并且每个对象只有一份,不会共享
局部变量 1:一般声明在方法中,作用域一般是声明时候的作用域,例如:如果直接声明到方法中,作用于是整个方法体,如果声明在方法的for循环中,则作用域在for循环中
2:局部变量没有默认初始值,因此在使用时必须先初始化
什么时候使用static修饰变量? 1:由于static修饰的变量在内存中只有一份,只要有任何对象对其进行修改就会改变值,所以一般我们会在static变量前面添加一个final关键字,已达到禁止修改
2:只有在工具类中,为了方便使用,才进行final static修饰变量,例如Math类中的常量PI
public class Cat { int age; static String name; final static String sex = "女"; } public static void main(String[] args) { Cat c = new Cat(); //age属性则是一个对象只能改变自己的age值 c.age = 12; //name属性是静态的,只在常量池中有且仅有一个,所以不管那个对象改变 值,都会发生改变 c.name = "加肥1号"; Cat c1 = new Cat(); c1.age = 13; c1.name = "汤姆号"; //c1.sex = "男";会产生报错原因是sex使用final修饰,则禁止改变 System.out.println(c.age+" "+c1.name); System.out.println(c1.age+" "+c.name); }方法 1:将一些功能性代码封装起来,以便反复使用
2:所有代码都在一起比较凌乱,这是将每段代码都封装成一个方法,这样代码看起来会比较整齐
方法的语法:[ ]代表可选
[访问修饰符] [static] 返回值类型 方法名([形参列表…]){
方法体
}
访问修饰符 主要用来控制方法可以被哪些类使用
1:public 例:公交车 代表本项目中谁都可以使用
2:protected 例:校车 代表本包下所有类,以及其他包下的子类对象可以使用
3:无 例:专线车 代表本包下所有类可以使用
4:private 例:私家车 代表只有本类中才能使用
public void eat(){ System.out.println("public"); } protected void play(){ System.out.println("protected"); } void swim(){ System.out.println("空"); } private void song(){ System.out.println("private"); } //可以调用public修饰的方法 c1.eat(); //可以调用protected修饰的方法 c1.play(); //可以调用没有修饰符修饰的方法 c1.swim(); //不可以调用private修饰的方法 c1.song();返回值类型 决定好方法调用完之后,返回给调用者什么类型的值
1:写方法时,想返回给调用者什么类型的值,就写什么类型
2:如果方法有返回值,那么就在方法最后使用return关键字将数据返回给调用者
3:如果方法不用返回给调用者数据,则使用void关键字表示这个方法没有返回值
4:如果方法使用了void关键字,在方法最后就不用使用return返回值了
5:有返回值的方法,执行完成之后会变成相应的返回值,可以把它放到输出语句中,也可以使用变量接收方法的返回值
方法名 标识符之一,根据命名规则自定义命名
形参列表 调用方法时需要调用者传给什么值,就声明什么
如果方法中需要一些数据,这些数据需要调用者传给我们,就可以将这些数据定义到形参列表上
方法体 方法想实现什么功能就写该功能的代码
是否静态 1:被static修饰的方法,叫做静态方法,静态方法可以使用对象名调用,也可以使用类名调用
2:被static修饰的方法,无法直接访问实例成员的属性以及实例方法
3:不被static修饰的方法,叫做实例方法,实例方法只可以使用对象名调用
4:实例方法可以访问所有的变量以及方法
构造方法(构造器) 构造方法是为了专门创建对象而存在的
1: 构造方法没有返回值,因为它就是为了创建对象而存在的
2:构造方法必须创建自身类的对象,所以方法名必须与自身类一致
3:如果类中不写构造方法,则默认自动生成无参构造
4:构造方法可以重载
this 当前使用的对象,代码执行到哪个对象,this就代表哪个对象
1:使用this.属性是调用当前对象的属性
2:使用this.方法是调用当前对象的方法
3:this()是调用自身构造方法,注意:this()只能在构造方法中使用
什么情况下使用this? 当方法中局部变量和全局变量冲突时,要想使用全局变量就需要使用this关键字
面向对象三大特征: 封装 1:将一组对象的共有属性与行为抽象成一个类
2:将属性私有化,提供公开的方法来访问属性,方法的要求如下:
2.1: 赋值的方法名要set开头,后面跟上属性名,例setName
2.2:取值的方法要get开头,后面跟上属性名,例getName。由于获取值需要有返回值,所以取值的方法需要有返回类型,并且一定是无参的
3:将一组功能性代码,封装成方法以便反复使用
继承 子类可以通过使用extends关键字去指定自己的父类,然后继承父类非私有属性及方法
1:只支持单继承,也就是说类只能继承一个类
2:每个类可以被继承多次
3:类中每个类都有自己的父类,如果没有指定父类,那么默认继承Object类,所以Object类是所有类的超类(基类)
4:子类可以从父类继承方法,如果子类对继承的方法不满意可以自己重写这个方法,如果子类重写了这个方法,那么调用时会优先调用重写的这个方法
5:构造方法不能被继承
6:在创建子类对象时,JVM会先调用父类无参构造方法去创建父类的实例化对象,然后再去创建子类的实例化对象
7:如果父类没有无参构造方法,我们需要在子类的构造方法中使用super关键字来显示父类构造方法
super:代表了父类对象 super()表示调用了父类构造方法
super.属性表示调用了父类的属性
super.方法()表示调用了父类的方法
方法重写 1:是发生在父子类之间的,方法名需要一致
2:返回类型需要一致
3:形参列表需要一致
4:访问修饰符的权限需要比父类的高
当我们将对象通过输出语句输出的时候,对象会默认调用toString方法,我们只需要重写toString方法就可以在输出语句输出对象的时候,返回对象信息的字符串
final关键字 1:修饰在变量上,让其变成一个常量
2:修饰在类上,不能被继承,例String就是final修饰的类,不能被继承
3:修饰在方法上,final修饰的方法不能被子类重写
static关键字 1:修饰在变量上,变量变成静态成员,可以被类直接调用
2:修饰在方法上,方法变成静态方法,可以被类直接调用
3:修饰在类上,变成静态类
4:修饰在代码块上,叫做静态块
代码块(4种) 1:静态块 直接写在类中,只有类在第一次被加载时才执行一次
2:构造块 直接写在类中,每次创建对象时,都会调用一次
3:普通块 写在方法之后,可以避免方法中变量的冲突,同时局限了变量的作用域
4:同步块 多线程时候再解释
静态块——优先所有——>构造块——优先自身——>构造方法
抽象类 1:被abstract class修饰
2:可以有普通类的一切(实例变量、静态变量、实例方法、静态方法、构造器、代码块等),以及抽象方法
3:有构造器,但不能实例化对象
4:可以继承于其它类,但不能被final修饰,因为抽象类中可能存在抽象方法
抽象方法 1:被abstract修饰
2:并且没有方法体
3:子类必须实现父类的抽象方法,除非子类也是抽象的
4:抽象方法只能定义在抽象类或接口中
5:抽象方法不能是private的,并且不能是static的,final的,synchronize的,native的
什么情况下使用继承 1:实际开发中很少使用继承,因为Java中的继承是单根的,如果随便继承一个父类,代码开发中如果需要继承其他的类,此时只能推翻重写,所以尽量从一开始就避免使用继承,只有在迫不得已的情况下才使用继承
2:如果真的需要使用继承的话,父类尽量定义成抽象类
继承的好处 1:有时候父类的功能真的很强大,我们需要继承里面的一些功能。例:HttpServlet
2:为了统一规范(Animal抽象类,所有的狗、猴子、老虎、狐狸都可以去继承他们共有的行为eat)
接口 接口与抽象类的区别 interface:用来标识接口
class:用来标识类
1:接口中只能有常量,并且接口中的常量默认必须被public static final修饰,而抽象类中可以有成员变量
2:接口中的实例方法必须被default修饰,并且访问修饰符默认必须是public,而抽象类实例方法随意
3:接口中静态方法默认必须是public修饰符,而抽象类随意
4:接口中没有构造器,而抽象类中有
5:接口中没有静态块、构造块,而抽象类中全有
6:接口中抽象方法默认被public abstract修饰,而抽象类需要手写public abstract
7:接口是被类用来实现的(普通类实现接口,必须添加所有的抽象方法实现),而抽象类是用来被继承的
8:接口可以被多实现,而抽象类只能单继承
9:接口可以多继承接口,而抽象类只能单继承
总结 1:接口对比起抽象类来讲更加抽象(接口可以理解成抽象类的升级抽象版)
2:一个类可以实现多个接口,并不影响继承
3:如果能使用接口,就尽量使用接口不要使用继承(尽量使用接口代替抽象类)
接口的用途 1:主要用来充当方法的目录,以及统一所有实现类中方法的规范
2:在以后的项目中,我们可能会使用很多常量值,这时就可以把这些常量定义到接口中,方便使用
重点:虽然JDK1.8之后接口中的方法可以有方法体了,但是为了保持接口的抽象我们一般不准在接口内写实例方法。
多态 就是指多个形态,所谓的多态就是指引用数据类型的向上转换,将子类的实例化向上转成了父类的类型,既然存在向上转型,就同时存在向下转型,向下转型是指将父类的类型强转成子类的类型
1:多态是指声明的是父类(包括接口)的类型,但是实例化的却是子类(实现类)的对象
2:继承或实现+方法的重写是多态的前提条件
多态的好处 1:可替换性
2:可扩充性(接口或父类新增实现类或子类不影响之前的代码结构)
3:灵活性
4:简化性
多态的缺点 1:当我们使用多态时,只能调用子类重写父类的方法,无法调用自身独有的方法
2:如果需要调用子类自己的方法,需要进行向下转型的 *** 作
instanceof关键字 可以判断对象是否可以转换成对应的类型,如果可以返回true,不可以返回false
API 应用程序接口,API实际上就是一个应用程序的说明书,用于查找工具类的方法
异常体系 Thorwable 所有异常以及错误体系的顶级父类,其下面有两个子类分别是Error、Exception
Error 表示的是错误,一般都是指硬件出现问题,或者程序结构出现问题,这种错误程序员无法通过代码捕获,而且一般情况下无法通过代码解决。例如:JVM内存不足、程序运行中断电、JVM内部出现问题等物理问题
Exception 表示异常,一个合理的应用程序应该去捕获并且处理异常,异常分为两种编译期异常、运行期异常
运行期异常 在代码编译的过程中,程序不会报错,但是在运行的过程中可能会出现异常情况
1、数组越界异常:java.lang.ArrayIndexOutOfBoundsException
2、算数异常:java.lang.ArithmeticException
3、空指针异常:java.lang.NullPointerException
特点:当别人调用方法的时候,不会强制让调用者处理异常
编译期异常 编译器异常是指在程序编译的阶段就必须处理的异常,如果不处理的话就会出现编译错误
特点:当别人调用方法的时候,需要强制让调用者处理异常
异常的处理 try catch捕获处理异常 try{
写有可能出现异常的代码
}catch(捕捉异常类型 对象名){
处理异常的代码
}
public static void b(){ try{ Class.forName("com.acv"); }catch(ClassNotFoundException e){ e.printStackTrace(); }finally{ System.out.println("抛出异常"); } }在方法体上面使用throws关键字向上抛出异常,抛给调用者进行处理,如果是在main方法中抛出,会抛给JVM虚拟机自动处理异常使用throws关键字抛异常 public static void main(String[] args) throws ClassNotFoundException{ a(); } public static void a() throws ClassNotFoundException{ Class.forName("com.zhiyou"); } 在方体中抛出异常 public static void main(String[] args){ b(); } public static void b(){ try{ Class.forName("com.acv"); }catch(ClassNotFoundException e){ e.printStackTrace(); }finally{ System.out.println("抛出异常"); } }为什么处理异常 当程序出现异常之后,如果程序员没有捕获异常,那么异常会由JVM虚拟机自动处理,JVM处理方式:
1、打印异常信息
2、关闭虚拟机。这样会造成程序运行中断,而如果程序员捕获了对应的异常信息,那么就会由程序员写的catch代码块来处理异常,JVM不再插手
finally在什么情况下执行 1、使用System.exit(0);关闭虚拟机
2、还没执行到finally的时候,因为意外或各种情况造成虚拟机关闭或系统错误
3、在执行try之前就碰到return。则不会执行finally
在try里面有一个return,请问return和finally谁先执行 return分为两步执行,1、先将需要返回的数据存入到内存之中 2、执行finally的代码3、继续执行return将内存中的值返回给调用者
为什么要有异常 异常实际上是一个类似警告牌一样的功能,在方法中合理创建异常对象可以提醒调用者需要注意的内容,以及在方法调用者出现问题的时候可以给出解决提示
异常处理流程 1、当程序运行过程中出现异常之后,JVM会检测当前出现异常的代码是否存在于try作用域之内
2、代码不在try作用域,JVM会自己捕获异常然后进行处理
3、JVM处理异常的方式先打印异常的相关信息,然后关闭虚拟机
4、如果代码存在于try作用域,JVM会将异常对象的类型按照catch的书写顺序与所有的catch一个一个进行比对,如果所有的catch全都匹配不成功,JVM会自己处理异常
5、如果catch匹配成功,JVM会将异常对象注入到catch域的形参之中然后执行catch作用域代码
6、当catch作用域代码执行完成后,继续执行finally里面的代码,然后继续往下执行下面的代码
自定义异常 RuntimeException:是所有运行期异常的父类,所有的运行期异常全部都继承于RuntimeException,如果我们需要自定义异常,只需要继承RuntimeException即可
public class MyRuntimeException extends RuntimeException{ public MyRuntimeException(String s){ super(s); } public static void main(String[] args) { try { method(1000); } catch (Exception e) { e.printStackTrace(); } } public static void method(int i) throws MyRuntimeException{ if(i>100){ throw new MyRuntimeException("传入的值不能大于100"); } System.out.println("代码正常执行"); System.out.println(i+100); } Exception:所有异常的父类,其和所有除了RuntimeExcep的子类都是编译期异常,如果我们需要自定义编译期异常只需要继承Exception即可
public class MyException extends Exception{ public MyException(String s){ super(s); } } public static void main(String[] args) { try { method(11); } catch (MyException e) { e.printStackTrace(); } } public static void method(int i) throws MyException{ if(i > 100){ throw new MyException("输入的值不能大于100,当前值为:"+i); } System.out.println("正常运行"); System.out.println(i+1); } throw:用于在方法之内,抛出异常对象给虚拟机,如果想在写方法的时候抛出异常对象必须使用throw才可以,简单点说,throw就是用来创建异常对象的
throws:用于方法体之上,将方法可能出现的异常交由方法的调用者进行处理,这个关键字自身不会产生异常对象,简单点说,throws是一种消极的解决异常的方式
当我们在写一些方法提供别人使用的时候,可以用throw去创建一些编译器异常,用于提醒方法调用者一些信息,此时我们需要使用throws将异常交给方法调用者处理,才能让对方注意到
泛型 将类中属性类型的确定工作推迟到实例化对象的时候来决定
public static void main(String[] args) { A基本数据类型与包装类 自动装箱与自动拆箱a = new A (); a.t = new Dog(); System.out.println(a); A c = new A<>(); c.t = 123; System.out.println(c); } class A { T t; @Override public String toString() { return "A [t=" + t + "]"; } } class Dog{ private int id; private String name; public int getId(){ return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setId(int id) { this.id = id; } @Override public String toString() { return "Dog [id=" + id + ", name=" + name + "]"; } 自动装箱:基本数据类型转换成了包装类对象
Integer i1 = new Integer(15); Integer i2 = new Integer(15); //18是基本数据类型,i3、i4是包装类,所以进行了自动装箱 Integer i3 = 18; Integer i4 = 18; 自动拆箱:包装类的对象变成了基本数据类型 例如,当我们使用包装类对象与基本数据类型相比较时,JVM会将包装类对象自动拆箱为基本数据类型再进行比较
System.out.println((i1 == 15)+".....");//true自动装拆箱发生的情况 1:将基本数据类型存入到集合中
2:基本数据类型与包装类对象进行比较
3:包装类对象进行运算的时候
4:三目运算符中使用
5:方法形参、方法返回值
手动装箱与手动拆箱 手动装箱
Integer i = Integer.valueOf("132");//手动装箱 手动拆箱,手动拆装箱主要用于不同数据类型之间的转换
Short j = i.shortValue();//手动拆箱String、StringBuffer和StringBuilder之间的区别 string:相同情况下执行时间最长,是不可变字符串,不断在常量池里面创建新的字符串,运行多少次,就创建多少个对象
StringBuffer:相同情况下执行第二少,可变字符串,无论改变多少次,还是一个对象,不会创建多个对象,耗费的时间主要是值的改变上
StringBuilder:功能与StringBuffer一致,API也一致,区别在StringBuilder线程是不安全的,StringBuffer线程是安全的,StringBuffer速度略慢与StringBuilder
当字符串的值不需要改变时,用String;当值要经常改变时,要使用StringBuilder或StringBuffer
String s = "张三"; StringBuilder sb1 = new StringBuilder(); StringBuffer sb = new StringBuffer(); long l = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { sb.append(i); s += i; sb1.append(i); } System.out.println("间隔时间:"+(System.currentTimeMillis()-l));集合的体系 三大类:list、set以及map,其中list与set都是单列集合,map是key、value键值对
名称 类型 作用 Iterable接口里面封装一个迭代器对象,以及一个forEach迭代方法 Iterator接口迭代器的顶层接口,定义了迭代器的规范 Collection接口所有单列集合的顶级接口,里面定义了所有单列集合共有的功能,并且继承了Iterable的迭代器 List接口有序可重复单列集合的顶级接口,里面定义了所有有序单列集合的共有功能,并且继承了Collection Set接口无序不重复单列集合的顶级接口,继承了Collection Queue接口队列的顶级接口,继承于Collection AbstractCollection抽象类对Collection接口提供了基本的实现 AbstractList抽象类对List接口的内容提供了基本的实现,同时继承了AbstractCollection ArrayList集合类继承于AbstractList,并且实现了List接口 特点:1、底层完全由数组实现,有序并且可重复 2、默认初始容量为10,每次扩容1.5倍,可以通过构造方法指定初始容量 3、由于存在下标,所以查询和修改速度加快,但是删除和插入较慢 4、线程不安全,效率较快 Vector集合类继承于AbstractList,并且实现了List接口 特点:1、底层完全由数组实现,有序并且可重复 2、默认初始容量为10,每次扩容1.5倍,可以通过构造方法指定初始容量 3、由于存在下标,所以查询和修改速度加快,但是删除和插入较慢 4、线程安全,效率较慢 AbstractSequentialList继承于AbstractList,定义了一些为链表提供的顺序 *** 作 StackVector的子类,诞生于jdk1.0,基本没有使用 Deque接口是一个双端队列,继承于Queue接口 ArrayDequeDeque的实现类,是一个双端队列的集合,同时继承了AbstractCollection linkedList集合类实现了Deque队列接口以及List接口的同时,又继承了AbstractSequentialList 特点:1、底层是由双线链表+双端队列实现 2、由于链表的底层不存在原生下标,所有查询和修改较慢(没有下标,需要不断遍历),插入和删除较快(没有下标,不需要维护) AbstractSet抽象类对Set接口的内容提供了基本的实现,同时继承了AbstractCollection以及实现了Set接口 HashSet集合类实现了Set接口,同时继承了AbstractSet 特点:1、无序的,并且值不能重复 2、在代码中创建了一个HashMap对象,将值存入到Map的Key上,所有 *** 作都是通过HashMap完成 SortedSet接口里面定义了一些比较方面相关的内容,想为Set提供排序的方法 NavigableSet接口完善了SortedSet比较和排序的方法规范,继承了SortedSet TreeSet集合类实现了NavigableSet接口,同时继承了AbstractSet 特点:1、可以按照书写规则对元素进行排序,值不能重复 HashMap如何保证key值不重复。(HashSet如何保证值不重复) 1:先通过HashCode求出一个数值,如果这个数值跟已有的冲突的话,会调用key的equals方法来判断是否真的出现hash冲突,还是重复插入
2:如果出现hash冲突则会使用链表形式存储出现的hash冲突的元素
hashCode方法的唯一目的,就是根据所有属性的值,计算出来一个数字 1:由于这个数字是根据属性的值来生成,所以如果对象的值一样,生成的数字肯定一致
2:由于hash冲突存在,有可能虽然属性的值不同,但是有可能生成了一样的数字
@Override public int hashCode() { // 声明一个局部常量31 final int prime = 31; // 定义记录总数的值 int result = 1; //根据age属性的值,求出了一个新值 result = prime * result + age; result = prime * result + ((name == null ? 0:name.hashCode())); //根据name属性更新了值 return result; }调用equals方法来判断是否出现hash冲突@Override public boolean equals(Object obj) { // 自己与自己进行对比,那值肯定一致,所以直接返回true if (this == obj) { return true; } // 空代表没有东西,直接false没必要比较 if (obj == null) { return false; } // 排除obj是其他类对象的可能性 if (getClass() != obj.getClass()) { return false; } // 在这里确定了obj是当前类的对象了,就可以对其进行向下转型 Dog d = (Dog) obj; // 双方age不相等 if (age != d.age) { return false; } // 自身的name是空 if (name == null) { // 两者name不相等 if (d.name != null) { return false; } } else if (!name.equals(d.name)) { return false; } return true; }TreeSet如何进行排序的 需要实现Comparable接口,然后在里面通过compareTo方法来完成排序,排序的规则为:返回正数排在后面,返回负数排在前面,返回0的话TreeSet会认为是相同的对象,造成添加数据失败
@Override public int compareTo(Dog d) { if(this.age>d.age){ return 1; }else if(this.age < d.age){ return -1; }else{ if(this.name.hashCode()>d.name.hashCode()){ return 1; }else if(this.name.hashCode() < d.name.hashCode()){ return -1; } } return 0; }tractSet 特点:1、可以按照书写规则对元素进行排序,值不能重复 |
HashMap如何保证key值不重复。(HashSet如何保证值不重复) 1:先通过HashCode求出一个数值,如果这个数值跟已有的冲突的话,会调用key的equals方法来判断是否真的出现hash冲突,还是重复插入
2:如果出现hash冲突则会使用链表形式存储出现的hash冲突的元素
hashCode方法的唯一目的,就是根据所有属性的值,计算出来一个数字 1:由于这个数字是根据属性的值来生成,所以如果对象的值一样,生成的数字肯定一致
2:由于hash冲突存在,有可能虽然属性的值不同,但是有可能生成了一样的数字
@Override public int hashCode() { // 声明一个局部常量31 final int prime = 31; // 定义记录总数的值 int result = 1; //根据age属性的值,求出了一个新值 result = prime * result + age; result = prime * result + ((name == null ? 0:name.hashCode())); //根据name属性更新了值 return result; }调用equals方法来判断是否出现hash冲突@Override public boolean equals(Object obj) { // 自己与自己进行对比,那值肯定一致,所以直接返回true if (this == obj) { return true; } // 空代表没有东西,直接false没必要比较 if (obj == null) { return false; } // 排除obj是其他类对象的可能性 if (getClass() != obj.getClass()) { return false; } // 在这里确定了obj是当前类的对象了,就可以对其进行向下转型 Dog d = (Dog) obj; // 双方age不相等 if (age != d.age) { return false; } // 自身的name是空 if (name == null) { // 两者name不相等 if (d.name != null) { return false; } } else if (!name.equals(d.name)) { return false; } return true; }TreeSet如何进行排序的 需要实现Comparable接口,然后在里面通过compareTo方法来完成排序,排序的规则为:返回正数排在后面,返回负数排在前面,返回0的话TreeSet会认为是相同的对象,造成添加数据失败
@Override public int compareTo(Dog d) { if(this.age>d.age){ return 1; }else if(this.age < d.age){ return -1; }else{ if(this.name.hashCode()>d.name.hashCode()){ return 1; }else if(this.name.hashCode() < d.name.hashCode()){ return -1; } } return 0; }欢迎分享,转载请注明来源:内存溢出
评论列表(0条)