Java核心笔记
0.导论
标柱注释:
- 单独重点加粗
- 单独背诵下划线
- ⭐️重点
- ⭐️⭐️重点的重点
学习方法:
- 需求——>知识点——>基本原理语法——>快速入门(基本程序)——>技术细节
1 java概述
1.1转义字符
- \t 一个制表位,实现对齐功能
- \n 换行
- \\ 一个\
- \" 一个"
- \r 回车
1.2注释
- 单行注释: //注释文字
- 多行注释: /*注释文字*/
- 文档注释:
/** * @author * @version */
1.3代码规范⭐️
- 类、方法的注释,要以 javadoc 的方式来写
- 注释详细,着重告述读者为什么这样写,如何修改,注意什么问题等
- 使用tab、shift + tab
- 运算符和 = 两边习惯性各加一个空格
- 源文件使用utf-8编码
- 行宽度不要超过80字符
- 代码编写次行风格和行尾风格
1.4 JDK JRE⭐️
- JDK(java开发工具包)
- JDK = JRE + java开发工具
- JRE = JVM + Java的核心类库
- JVM(java虚拟机)
2 变量
2.1 变量
- 变量相当于内存中一个数据存储空间的表示,你可以把变量看做是一个房间的门牌号,通过门牌号我们可以找到房间,而通过变量名可以访问到变量(值)
基本介绍
int age = 20; double score = 88; char gender = '男'; String name = "jack";
注意事项和细节:
- 变量表示内存中的一个存储区域[不同的变量,类型不同,占用的空间大小不同]
- 该区域有自己的名称[变量名]和类型[数据类型]
- 变量必须先声明,后使用,即有顺序
- 该区域的数据可以在同一类型范围内不断变化
- 变量在同一个作用域内不能重名
- 变量=变量名+值+数据类型
2.2 基本数据类型⭐️
2.2.1 数值型
整数类型 (byte [1] short[2] int[4] long [8])
- Java的整型常量(具体值)默认为 int 型,声明long型常量须后加‘l’或‘L’
- java程序中变量常声明为int型,除非不足以表示大数,才使用long
- bit: 计算机中的最小存储单位,byte:t算机中基本存储单元,1byte = 8 bit
浮点(小数)类型 (float [4] double [8])
- 浮点数=符号位+指数位+尾数位
- 尾部可能丢失,造成精度损失
- Java 的浮点型常量(具体值)默认为double型,声明float型常量,须后加'f'或'F'
- 十进制数形式:5.12 512.0f .512(必须有小数点)科学计数法形式:5.12e2 5.12E-2
- 通常情况默认使用double
字符型(Char[2])
- 使用单引号表示
- Java中还允许使用转义字符来将其后的字符转变为特殊字符型常量
- char的本质是一个整数,输出时是unicode码对应字符
- char类可以进行运算
布尔型(boolean[1])
- boolean类型数据只允许取值true和false,无null
- boolean类型占1个字节
- 不可以用0或非0的整数代替false和true,与C语言不同
2.3 基本数据类型转化
2.3.1 自动类型转换
- java程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型
基本介绍:
int = 'c'; double d = 80
- char<int<long<float<double
byte<short<int<long<float<double
转换规则:
注意事项和细节
- 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,再进行计算
- 我们把精度(容量)大的数据类型赋值给精度(容量)小 的数据类型时,就会报错,反之就会进行自动类型转换。
- byte short char 之间不会相互转换
- byte short char 计算是首先转换为int类型
- boolean不参与转换
- 自动提升原则:表达式结果的类型自动提升为操作数中最大的类型
2.3.2 强制类型转换
- 自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符(),但可能造成精度降低或溢出,格外要注意
基本介绍:
int i = (int)8.8; System.out.println(i);
注意事项和细节
- 当数据从精度 大——>小,就需要使用到强制转换
- 强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
int y = int(10*3.5+6*1.5)
- char类型可以保存 int的常量值,但不能保存int的变量值,需要强转
int m = 100; char c2 = m;//false char c3 = (char)m;//ture
- byte short char 类型在进行运算时,当做int类型处理
2.4 基本数据类型和String类型转换
- 语法:将基本类型的值 +” “
基本类型转String类型
int n1 = 1 float n2 = 1.1f; double n3 = 3.4; boolean b1 = true; String s1 = n1 + ""; String s2 = n2 + ""; String s3 = n3 + ""; String s4 = b1 + "";
- 语法:通过基本类型的包装类调用parseXX方法
String类型转基本数据类型
String s5 = "123"; int num1 = InterInt.parseInt(s5); int num2 = InterInt.parseDouble(s5); int num3 = InterInt.parseFloat(s5);
3 运算符
3.1 算数运算符
- 算术运算符是对数值类型的变量进行运算的
基本介绍:
3.1.1+号使用
- 当左右两边都是数值型时,则做加法运算
- 当左右两边有一方为字符串,则做拼接运算
System.out,println(100 + 98): //198 System.out.println("100" + 98);//10098 System.out.println(100 + 3 +"hello");//103hello System.out.println("hello"+ 100 +3);//hello1003
3.1.2 ++号使用
- 前++和后++都完全等价子 i=i+1;作为表达式使用前++:++先自增后赋值后++:i++先赋值后自增
3.1.3 %号使用
- a % b 当a是小数时,公式 = a - (int)a / b * b
3.2 关系运算符
- 关系运算符的结果都是 boolean 型
3.3 逻辑运算符
- 用于连接多个条件(多个关系表达式),结果是boolean
基本介绍:
3.3.1 &&和&基本规则
- 如果第一个条件为 false ,后面的条件不再判断
&& 短路与:
- 如果第一个条件为 false ,后面的条件仍然判断
& 逻辑与:
3.3.2 ||和|基本规则
- 如果第一个条件为 true,则第二个条件不会判断,结果为true
|| 短路或:
- 不管第一个条件是否为true,第二个条件都要判断
| 逻辑或:
3.3.3 ^逻辑异或
- 当a和b不同时,则结果为true,否则为false
3.4 赋值运算符
- 赋值运算符就是将某个运算后的值,赋给指定的变量
基本介绍:
注意事项和细节:
- 运算顺序从右往左
- 赋值运算符的左边只能是变量,右边可以是变量、表达式、常量值
- 复合赋值: a x= b 等于 a = a x b
- 复合赋值运算符会进行类型转换
byte b = 3;
b += 2; //等价于 b = (byte)(b + 2)
b++; //等价于 b = (byte)(b + 1)
3.5 三元运算符
基本语法:
运算规则:
- 如果条件表达式为true,运算后的结果是表达式1;
- 如果条件表达式为false,运算后的结果是表达式2;
注意事项和细节:
- 表达式1和表达式2要为可以赋给接收变量的类型(或可以自动转换)
- 三元运算符可以转成 if--else语句
条件表达式?表达式1:表达式2;
int a = 10; int b = 99; int result = a>b ? a++:b--; int c = a > b ? int(1.1) : int(3.4);
3.5 运算符优先级
优先级:
- () {} , 等
- 单目运算符 ++ --
- 算术运算符
- 位移运算符
- 比较运算符
- 逻辑运算符
- 三元运算符
- 赋值运算符
3.6 标识符的命名规则和规范
规则:
- 由26个英文字母大小写,0~9,或$组成
- 数字不可开头
- 不可以使用关键字和保留宇,但能包含关键字和保留字
- Java中严格区分大小写,长度无限制
- 标识符不能包含空格
规范:
- 包名:多单词组成时所有字母都小写:aaa.bbb.ccc
- 类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
- 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单河开始每个单词首字母大写:xxxYyyzzz
- 常量名:所有字母都大写,多单词时每个单词用下划线连接:XXX_YYY_ZZZ
3.7 关键字、保留字
- 被Java语言赋子了特殊含义,用做专门用途的字符串(单词)特点:关键字中所有字母都为小写
关键字基本介绍:
- 现有Java版本尚未使用,但以后版本可能会作为关键字使用。自己命名标识符时要避免使用这些保留宇byValue、cast、future、 generic、 inner、 operator、outer、rest、var、goto、const
保留字基本介绍:
3.8 键盘输入
- 在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取lnput.java,需要一个扫描器(对象),就是 Scanner
基本介绍:
步骤:
- 导入该类的包,java.util.*
- 创建该类的对象(声明变量)
- 调用里面的功能
import java.util.Scanner; public class hello { public static void main(String []args) { Scanner myScanner = new Scanner(System.in);//System.in 代表使用键盘输入 System.out.println("请输入名字"); String name = myScanner.next(); System.out.println("请输入年龄"); int age = myScanner.nextInt(); System.out.println("名字="+name+ "\t" +"年龄="+age); } }
3.9 进制⭐️
- 二进制:0.1,满2进1,以0b或0B开头
- 十进制:0-9,满10进1
- 八进制:0-7,满8进1,以数字0开头表示
- 十六进制:0-9及A(10)-F(15),满16进1.以Ox或0x开头表示,此处的A-F不区分大小写
3.9.1 其他转十进制
- 从最低位开始,将每个位上的数提取出来,乘以(几进制)的(位数-1)次方例:0b01011 = 1 * 2^(1-1) + 1 * 2^(2-1) + 0 * 2^(3-1) + 1 * 2^(4-1) = 1 + 2 + 0 + 8 =11
3.9.2 十进制转其他
- 将该数不断除(几进制),直到商为0,每步余数倒写
3.9.3 二进制转八/十六
- 从低位开始,每三位一组,转成对应八进制
- 从低位开始,每四位一组,转成对应十六进制
3.9.4 八/十六进制转二
- 将八进制的每一位,转成对应的一个三位的二进制数
- 将十六进制的每一位,转成对应的一个四位的二进制数
3.10 原码 反码 补码⭐️⭐️
运算规则:
- 二进制的最高位是符号位:0表示正数,1表示负数
- 正数的原码、反码、补码都一样(三码合一)
- 负数的反码 = 它的原码符号位不变,其它位取反
- 负数的补码 = 它的反码+1,负数的反码 = 负数的补码 - 1
- 0的反码,补码都是0
- java中的数都是有符号的
- 计算机运算的时候,都是以补码的方式来运算的
- 看运算结果的时候,要看他的原码
3.11 位运算符⭐️
位运算符
- 按位与 &
- 按位或 |
- 按位异或 ^
- 按位取反 ~
- 算数右移 >> 低位溢出,符号位不变,并用符号位补溢出的高位(本质/2)
- 算数左移 << 符号位不变,低位补0(本质*2)
- 逻辑右移 >>> 低位溢出,高位补0
位运算过程:
- 用原码得到补码
- 用补码进行相关逻辑运算
- 运算完毕将补码转换成原码
4.2 分支控制(if else switch)
4.2.1 单分支
基本语法:
if(条件表达式){ 执行代码块; }
注意事项和细节:
- 当条件表达式为ture 时,就会执行{}的代码。如果为false,就不执行
- 如果{}中只有一条语句,则可以不用{},建议写上{}
4.2.2 双分支
基本语法:
if(条件表达式){ 执行代码块; }else{ 执行代码块2; }
注意事项和细节:
- 当条件表达式成立,即执行代码块1,否则执行代码块2
- 如果执行代码块有一条语句,则{}可以省略,否则,不能省略
4.2.3 多分支
基本语法:
if(条件表达式){ 执行代码块; }else if(条件表达式2){ 执行代码块2; } ... else{ 执行代码块n; }
注意事项和细节:
- 当条件表达式1成立时,即执行代码块1
- 如果表达式1不成立,才去判断表达式2是否成立
- 如果表达式2成立,就执行代码块2
- 以此类推,如果所有的表达式都不成立则执行else的代码块
- 多分支可以没有else
4.2.4 嵌套分支
- 在一个分支结构中又完整的嵌套了另个完整的分支结构,里面的分支的结构称为内层分支外面的分支结构称为外层分支
基本介绍:
基本语法:
if(){ if(){ //if-else }else{ //if-else } }
4.2.5 switch分支结构
基本语法
switch(表达式){ case 常量1: 语句块1; break; case 常量2: 语句块2; break; default: 语句块; break; }
说明:
- switch 关键字,表示swtich分支
- 表达式对应一个值
- case 常量1:当表达式的值等于常量1,就执行语句块1
- break :表示退出swtich
- 如果和 case 常量1匹配,就执行语句块1,如果没有匹配,就继续匹配 case 常量2
- 如果一个都没有匹配上,执行default
注意事项和细节:
- 表达式数据类型,应和case 后的常量类型一致,或者是可以自动转成可以相互比较的类型,比如输入的是字符,而常量是 int
- switch(表达式)中表达式的返回值必须是:(byte,short.int,char,enum,String)
- case子句中的值必须是常量,而不能是变量
- default子句是可选的,当没有匹配的case时,执行default
- break语句跳出switch语句块,如果没有break,程序会执行到结尾
4.3 循环控制(for while do while)⭐️
4.3.1 for循环控制
基本语法:
for(循环变量初始化;循环条件;循环变量迭代){
循环操作语句;
}
说明:
- 四要素:(1)循环变量初始化(2)循环条件(3)循环操作(4)循环变量迭代
- 循环操作,这里可以有多条语句,也就是我们要循环执行的代码
- 如果 循环操作(语句) 只有一条语句,可以省路{},建议不要省略
注意事项和细节:
- 循环条件是返回一个布尔值的表达式
- for(;循环判断条件;)中的初始化和变量迭代可以写到其它地方,但是两边的分号不能省略
- 循环初始值可以有多条初始化语句,但要求类型一样,井且中间用逗号隔开
- 循环变量迭代也司以有名条变量迭代语句,中间用逗号隔开
4.3.2 while循环控制
基本语法
循环变量初始化;
while(循环条件){
循环体;
循环变量迭代;
}
说明:
- 四要素:(1)循环变量初始化(2)循环条件(3)循环操作(4)循环变量迭代
- 四要素位置不同
注意事项和细节:
- 循环条件是返回一个布尔值的表达式
- while循环是先判断在执行语句
4.3.3 do while循环控制
基本语法
循环变量初始化;
do{
循环体;
循环变量迭代;
}while(循环条件);
说明:
- 四要素:(1)循环变量初始化(2)循环条件(3)循环操作(4)循环变量迭代
- 四要素位置不同
- 先执行在判断,至少执行一次
- 最后有一个;
注意事项和细节:
- 循环条件是返回一个布尔值的表达式
- 先执行在判断,至少执行一次
4.3.4 多重循环控制
介绍:
- 将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for,while,do.while均可以作为外层循环和内层循环
- 嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为false时,才会完全跳出内层盾环,才可结束外层的当次循环,开始下一次的循环
- 设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次
4.4 跳转控制break
- break语句用于终止某个语句块的执行,一般使用在switch或者循环[for,while,do while]
基本介绍:
注意事项和细节:
- break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块
- 标签的基本使用
label1:{......
label2:{
break label1;
}
}
4.5 跳转控制continue
基本介绍:
- continue语句用于结束本次循环,继续执行下一次循环
- continue语句出现在多层嵌套的福环语句体中时,可以通过标签指明要跳过的是哪一层循环
注意事项和细节:
- continue语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块
- 标签的基本使用
label1:{...... label2:{ continue label1; } }
4.6 跳转控制return
- return使用在方法,表示跳出所在的方法
基本介绍:
5 数组、排序、查找
5.1 数组⭐️
5.1.1 使用方式
1-动态初始化
- 数据类型 数组名 [] = new 数据类型[大小]
语法:
int[] a = new int[5];//创建一个数字名字为a,存放了五个int
- 数组名[下标/索引],下标从0开始
数组的引用:
2-动态初始化
- 数据类型 数组名[];也可以 数据类型[] 数组名;例:int a[];或者int[] a;
先声明数组:
- 数组名 = new 数据类型[大小];例:a = new int [10];
再创建数组:
3-静态初始化
- 数据类型 数组名[] = {元素值,元素值......}
语法:
注意事项和细节
- 数组是多个相同类型数据的组合
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用
- 数组创建后如果没有赋值,有默认值:int0,short 0, byte 0, long 0, float 0.0,double 0.0char \u0000,boolean false, String null
- 使用数组的步骤 1. 声明数组并开辟空间 2给数组各个元素赋值 3 使用数组
- 数组的下标是从0开始的
- 数组下标必须在指定范围内使用,否则报:下标越界异常
- 数组属引用类型,数组型数据是对象(object)
5.1.2 数组赋值机制
- 数组在默认情況下是引用传递,赋的值是地址
int arr1 = {1,2,3};
int arr2 = arr1;
arr2[0] = 10; //arr1[0]=10
5.1.3 数组拷贝
int[] arr1 = {1,2,3}; int[] arr2 = new int[arr1.length]; for (int i = 0;i < arr1.length;i++){ arr2[i] = arr1[i]; }
5.1.4 数组反转
规律反转
public class ArrayReverse01 { public static void main(String[] args) { int[] arr = {11,22,33,44,55,66}; int temp = 0,len = arr.length; for (int i = 0;i < len/2;i++){ temp = arr[len-1-i]; arr[len-1-i] = arr[i]; arr[i] = temp; } for(int i = 0;i < arr.length;i++){ System.out.print(arr[i]+"\t"); } } }
逆序赋值
public class ArrayReverse02 { public static void main(String[] args) { int[] arr1 = {11,22,33,44,55,66}; int[] arr2 = new int[arr1.length]; int len = arr1.length; for(int i = len-1;i >= 0;i--){ arr2[len-i-1] = arr1[i]; } arr1 = arr2;//arr1指向arr2数据空间,此时arr原来的数据空间没有变量引用,会被当作垃圾销毁 for(int i = 0;i < arr1.length;i++) { System.out.print(arr1[i] + "\t"); } } }
5.1.6 数组添加
静态添加
public class ArrayAdd01 { public static void main(String[] args) { int[] arr = {1,2,3}; int[] arr2 = new int[arr.length+1]; for (int i = 0;i < arr.length;i++){ arr2[i] = arr[i]; } arr2[arr2.length-1] = 4; arr = arr2; for(int i = 0;i < arr.length;i++) { System.out.print(arr[i] + "\t"); } } }
动态添加
import java.util.Scanner; public class ArrayAdd02 { public static void main(String[] args) { Scanner myScanner = new Scanner(System.in); double[] arr = {1,2,3}; do{ double[] arr2 = new double[arr.length+1]; for (int i = 0;i < arr.length;i++) { arr2[i] = arr[i]; } System.out.println("请输入添加的元素"); double addNum = myScanner.nextInt(); arr2[arr2.length-1] = addNum; arr = arr2; for(int i = 0;i < arr.length;i++) { System.out.print(arr[i] + "\t"); } System.out.println("是否继续添加 y/n"); char key = myScanner.next().charAt(0); if(key == 'n'){ break; } }while (true); } }
动态删减
import java.util.Scanner; public class ArrayReduce01 { public static void main(String[] args) { Scanner myScanner = new Scanner(System.in); int[] arr = {1,2,3,4,5}; while (true){ int[] arr2 = new int[arr.length-1]; for (int i = 0;i < arr2.length;i++){ arr2[i] = arr[i]; } arr = arr2; for(int i = 0;i < arr.length;i++) { System.out.print(arr[i] + "\t"); } System.out.println("请输入是否删除最后一个数字 yes/no"); char key = myScanner.next().charAt(0); if(key == 'n'){ break; } } } }
5.2 二维数组
- 一维数组构成了二维数组
基本介绍:
二维数组遍历
public class TwoDimensionalArrary { public static void main(String[] args) { int[][] arr = {{1,2,3},{4,5,6},{7,8,9}}; for (int i = 0;i < arr.length;i++){ for (int j = 0;j < arr[i].length;j++){ System.out.print(arr[i][j]+"\t"); }System.out.println(); } } }
-
网络异常,图片无法展示|
二维数组内存图
注意事项和细节:
- 二维数组元素个数:arr.length
- 取出一维元素需要遍历两次
- 访问第(i+1)个一维数组的第(j+1)个值 arr [i] [j]
5.2.1 使用方式
1-动态初始化
- 类型 数组名[] [] = new 类型 [大小] [大小]
语法:
public class TwoDimensionalArrary02 { public static void main(String[] args) { int arr[][] = new int[2][3]; arr[1][1] = 8; for(int i = 0;i < arr.length;i++){ for(int j = 0;j < arr[i].length;j++){ System.out.print(arr[i][j]+" "); } System.out.println(); } } }
2-动态初始化2
- 数据类型 数组名[] [];例:int a[] [];
先声明数组:
- 数组名 = new 数据类型[大小] [大小];例:a = new int [2] [3];
再创建数组:
3-动态初始化-列数不确定
public class TwoDimensionalArrary03 { public static void main(String[] args) { int[][] arr = new int[3][];//创建二维数组,只确定一维数组的个数,一维数组开没有开数据空间 for(int i = 0;i < arr.length;i++){ arr[i] = new int[i+1];//给一维数组开空间 for (int j = 0; j < arr[i].length;j++){ arr[i][j] = i+1;//给每一个一维数组元素赋值 } } for(int i = 0;i < arr.length;i++){ for (int j = 0; j < arr[i].length;j++){ System.out.print(arr[i][j]+" "); } System.out.println(" "); } } } //输出结果:1 22 333
4-静态初始化
- 类型 数组名[] [] = {{值1,值2},{值1,值2},{值1,值2}}
语法:
注意事项和细节:
- 一维数组声明方式:int [] x 或者 int x []
- 二维数组声明方式:int [] [] y 或者 int[] y [] 或者 int y [] []
- 二维数组实际上是由多个一维数组组成的,它的各个一维数组的长度可以相同,也可以不相同
5.3 排序(基础)
5.3.1 排序分类
- 想需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法)
- 数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)
5.3.2 冒泡排序
- 冒泡排序 (Bubble Sorting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒
定义:
冒泡排序特点
- 一共有n个元素
- 一共进行了n-1轮排序,可以看成是外层循
- 每1轮排序可以确定一个数的位置,比如第1轮排序确定最大数,第2轮排序,确定第2大的数位置,依次类推
- 当进行比较时,如果前面的数大于后面的数,就交换
public class BubbleSort { public static void main(String[] args) { int temp = 0; int[] arr = {24,69,80,57,13,321,34,56,7,-4}; for (int i = 0;i < arr.length-1;i++){ for(int j = 0;j < arr.length-1-i;j++){ if(arr[j] > arr[j + 1]){ temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } System.out.println("==排序结果=="); for(int i = 0;i < arr.length;i++) { System.out.print(arr[i] + "\t"); } } }
5.4 查找(基础)
5.4.1 顺序查找
import java.util.Scanner; public class SeqSearch { public static void main(String[] args) { String[] names = {"白眉鹰王","金毛狮王","紫衫龙王","青翼蝠王"}; Scanner myScanner = new Scanner(System.in); System.out.println("请输入名字"); String findName = myScanner.next(); int index = -1; for(int i = 0;i < names.length;i++){ if(findName.equals(names[i])){ System.out.println("恭喜找到"+findName); System.out.println("序号为"+(i+1)); index = 1; break; } } if(index == -1){ System.out.println("sorry,没有找到"+findName); } } }
5.4.2 二分查找
6 面向对象(基础)
6.1 类与对象
基本介绍:
- 类是抽象的,概念的,代表一类事物,比如人类, 猫类…即它是数据类型
- 对象是具体的,实际的,代表一个具体事物,即是实例
- 类是对象的模板,对象是类的一个个体,对应一个实例
-
网络异常,图片无法展示|
对象存在形式⭐️
6.1.1属性/成员变量
基本介绍:
- 从概念或叫法上看:成员变量 = 属性 =field(字段)
- 属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组),比如定义猫类 的 int age 就是属性
注意事项和细节:
- 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名
- 访问修饰符:控制属性和访问范围,有四种访问修饰符 public,proctected,defaul,private
- 属性的定义类型可以为任意类型,包含基本类型或引用类型
- 属性如果不赋值,有默认值,规则和数组一致:
int0,short 0, byte 0, long 0, float 0.0,double 0.0char \u0000,boolean false, String null
6.1.2如何创建对象
先声明在创建
Cat cat;
cat = new Cat();
直接创建
Cat = new Cat();
对象创建流程分析⭐️⭐️
- 加载Person类信息(就是Person.class 且只加载一次)
- 在堆中分配空间(地址)
- 完成对象初始化
(3.1默认初始化 age = 0 name =null
3.2显示初始化 age = 90 name = null
3.3构造器初始化 age = 20 name = 小倩) - 在对象在堆中的地址返回给P(P是对象名,是对象的引用)
6.1.3对象分配机制
Person p1 = new Person(); p1.age = 10; p1.name = "小明"; Person p2 = p1//把p1赋给了p2,或让p2指向p1 System.out.println(p2.age);
Java内存结构分析:
- 栈:一般存放基本数据类型(局部变量)
- 堆:存放对象(Cat cat,数组等)
- 方法区:常量池(常量,比如字符串),类加载信息
Java创建对象流程
- 先加载Person类信息(属性和方法信息,只会加载一次)
- 在堆中分配空间,进行默认初始化(看规则)
- 把地址赋给 p,p就指向对象
- 行指定初始化,比如 p.name =" jack" p.age = 10
Person p = new Person(); p.name = "jack"; p.age = 10;
6.2 成员方法⭐️
方法定义:
- 访问修饰符 返回数据类型 方法名(形参列表) {//方法体 语句; return 返回值;}
- 形参列表:表示成员方法输入 callint n)
- 数据类型(返回类型):表示成员方法输出,void 表示没有返回值
- 方法主体:表示为了实现某一功能代码块
- return 语句不是必须的
6.2.1 调用方法:
- public 表示方法是公开
- void:表示方法没有返回值
- speak() :speak是方法名,()形参列表
- {}方法体,可以写我们要执行的代码
- System.out.println(”我是一个好人”);表示我们的方法就是输出一句话
- p1.speak()为调用
public class Method01 { public static void main(String[] args) { Person p1 = new Person(); p1.speak(); p1.cal01(); p1.cal02(10); int returnRes = p1.getSum(1,2); System.out.println(returnRes); } } class Person{ String name; int age; public void speak(){ System.out.println("你是一个呆猪"); } public void cal01(){ int res = 0; for (int i = 1;i <= 1000;i++){ res += i; } System.out.println(res); } public void cal02(int n){ int res = 0; for (int i = 1;i <= n;i++){ res += i; } System.out.println(res); } public int getSum(int num1,int num2){ int res = num1 + num2; return res; } }
6.2.2 调用的内存机制:
调用的内存机制:
- public 表示方法是公开的
- int :表示方法执行后,返回一个 int 值
- getsum 方法名
- (int num1, int num2) 形参列表,2个形参,可以接收用户传入的两个数
- return res;表示把res 的值,返回
public int getSum(int num1,int num2){ int res = num1 + num2; return res;
6.2.3 注意事项和细节
访问修饰符:
- 作用是控制方法使用的适用范围,若果不写则默认访问
返回类型:
- 一个方法最多有一个返回值
- 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
- 方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值;要求返回值类型必须和return的值类型一致或兼容
- 如果方法是void,则方法体中可以没有return语句,或者 只写 return
方法名:
- 方法名遵循驼峰法则
参数列表:
- 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,比如 getSum(int n1,int n2)
- 参数类型可以为任意类型,包含基本类型或引用类型,比如 printArr(intlIl map)
- 调用参数的方法时,一定对应着参数列表传入相同类型或莱容类型 的参数
- 方法定义时的参数称为形式参数,简称形参;方法调用时的参数称为实际参数,简称实参,实参和形参的类型要一致或兼容、个数、顺序必须一致
方法体:
- 里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用
- 里面不能再定义方法!即:方法不能嵌套定义
方法调用细节:
- 同一个类中的方法调用:直接调用即可
- 跨类中的方法A类调用B类方法:需要通过对象名调用
- 跨类的方法调用和方法的访问修饰符相关
6.3 成员方传法参机制⭐️⭐️
6.3.1 基本数据类型的传参数机制
- 传递的是值(拷贝),形参的认何改变不影响实参
网络异常,图片无法展示|
6.3.2 引用数据类型的传参数机制
- 引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参
网络异常,图片无法展示|
网络异常,图片无法展示|
6.4 递归
- 递归就是方法自己调用自己,每次调用时传入不同的变量
基本介绍:
递归调用内存机制
public class Recursion01 { public static void main(String[] args) { T t1 = new T(); t1.test(5); int res = t1.factorial(5); System.out.println("res = "+ res); } } class T{ public void test(int n){ if(n > 2){ test(n - 1); } System.out.println(n); } public int factorial(int n){ if(n == 1){ return 1; }else { return factorial(n - 1)*n; } } }
注意事项和细节:
- 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
- 方法的局部变量是独立的,不会相互影响,比如n变量
- 如果方法中使用的是引用类型变量(比如数组、对象),就会共享该引用类型的数据
- 递归必须向退出递归的条件逼近,否则就是无限递归
- 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕,栈空间回收
6.5 方法重载 (overload)
- java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致
基本介绍:
注意事项和细节
- 方法名:必须相同
- 形参列表:必须不同(参数类型、个数、顺序)
- 返回类型:无要求
6.6 可变参数
- java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法,通过可变参数实现
基本概念:
基本语法:
访问修饰符 返回类型 方法名(**数据类型...** 形参名){}
注意事项和细节:
- 可变参数的实参可以为0个或任意多个
- 可变参数的实参可以为数组
- 可变参数的本质就是数组
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
- 一个形参列表中只能出现一个可变参数
6.7 变量作用域⭐️
全局变量和局部变量:
- 主要的变量就是属性(成员变量)和局部变量
- 局部变量一般是指在成员方法中定义的变量
- 全局变量:也就是属性,作用域为整个类体
- 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中
- 全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值
注意事项和细节:
- 全局变量和局部变量可以重名,访问时遵循就近原则
- 在同一个作用域中,两个局部变量,不能重名
- 全局变量生命周期长,伴随着对象的创建而创建,伴随着对象的销毁而销毁
- 局部变量生命周期短,伴随着它的代码块的执行而创建,伴随着代码块的结束而结束:即在一次方法调用过程中
- 作用域范围不同全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)局部变量:只能在本类中对应的方法中使用
- 修饰符不同全局变量/属性可以加修饰符局部变量不可以加修饰符
两种调用方式
public class VarScopeDetail { public static void main(String[] args) { E e1 = new E(); e1.test(); Per p1 = new Per(); e1.test2(p1); } } class E{ public void test(){ Per p1 = new Per(); System.out.println(p1.name); } public void test2(Per p){//接收一个类 System.out.println(p.name); } } class Per{ String name = "jack"; }
6.8 构造器⭐️
- 构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化
基本介绍:
基本语法:
[修饰符] 方法名 (行参列表){
方法体;
}
注意事项和细节:
- 修饰符可以默认
- 一个类可以定义多个不同的构造器,即构造器重载
- 构造器名和类名要相同
- 构造器没有返回值
- 构造器是完成对象的初始化,井不是创建对象
- 在创建对象时,系统自动的调用该类的构造方法
- 如果没有定义构造器,系统会自动给类生成一个默认无参构造器(默认构造器)
- 定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下
public class Constructor01 { public static void main(String[] args) { Pers p1 = new Pers(); Pers p2 = new Pers("jack",80); System.out.println(p1.name+p1.age); System.out.println(p2.name+p2.age); } } class Pers{ String name; int age; public Pers(){ System.out.println("构造器1被调用"); age = 18; } public Pers(String pName,int pAge){ System.out.println("构造器2被调用"); name = pName; age = pAge; } }
6.9 this
- java虚拟机会给每个对象分配this,代表当前对象
基本介绍:
-
网络异常,图片无法展示|
内存分析:
注意事项和细节:
- this关键字可以用来访问本类的属性、方法、构造器
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造器语法:this(参数列表):注意只能在构造器中使用(在构造器中访问另外一个构造器,必须放在第一条语句)
- this不能在类定义的外部使用,只能在类定义的方法中使用
public class ThisExercise01 { public static void main(String[] args) { person p1 = new person("marry",20); person p2 = new person("marry",20); System.out.println(p1.compareTo(p2)); } } class person{ String name; int age; public person(String name,int age){ this.name = name; this.age = age; } public boolean compareTo(person p){ return this.name.equals(p.name) && this.age == p.age; } }
7 面向对象(中级)
7.0 IDEA
常用快捷键:
- command + d 删除当前行
- command + option + 向下光标 向下复制当前行
- option + / 补全代码
- command + / 注释
- option + enter 导入该行的类
- command + option + L 格式化代码
- control + R 运行
- optio + A 构造器
- control + H 查看继承关系
- command + B 定位方法
- .var 自动变量名
- Command + option + T 环绕方式
常用模版快捷键
- fori 遍历
- itar 遍历数组
- iter 增强遍历
- sout 打印换行
7.1 包
- Package 包名
基本语法:
包的命名:
- 只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
命名规范:
- 小写字母+小圆点一般是 com.公司名.项目名.业务模块名
引入包:
- import java.uti Scanner;就只是引入一个类Scanner
- import java.util*:1/ 表示将java.util 包所有都引入
注意事项和细节:
- package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一个package
- import指令 位置放在package的下面,在类定义前面,可以有多句目没有顺序要求
7.2 访问修饰符
- java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围)
基本介绍:
访问权限⭐️⭐️:
- 公开级别:用public修饰,对外公开
- 受保护级别:用protected修饰,对子类和同一个包中的类公开
- 默认级别:没有修饰符号,向同一个包的类公开
- 私有级别:用private修饰,只有类本身可以访问,不对外公开
访问级别 |
访问修饰符 |
同类 |
同包 |
子类 |
不同包 |
公开 |
public |
✅ |
✅ |
✅ |
✅ |
受保护 |
protected |
✅ |
✅ |
✅ |
❌ |
默认 |
无 |
✅ |
✅ |
❌ |
❌ |
私有 |
private |
✅ |
❌ |
❌ |
❌ |
注意事项和细节:
- 修饰符可以用来修饰类中的属性,成员方法以及类
- 只有默认的和public才能修饰类,并目遵循上述访问权限的特点
- 子类待定
- 成员方法的访问规则和属性完全样
7.3 封装⭐️
- 封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作方法封装在一起数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作
基本介绍:
封装步骤:
- 将属性进行私有化private【不能直接修改属性】
- 提供一个公共的(public)set方法,用于对属性判断井赋值public void setxxx(类型 参数名){加入数据验证的业务逻辑;属性 = 参数名;}
- 提供个公共的(public)get方法,用于获取属性的值public 数据类型 getXxxx0{return XX;}
7.4 继承⭐️
- 继承可以解决代码复用,当多个类存在相同的属性(变量)和方法时,可以以这些类中抽象出交类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可
基本介绍:
基本语法:
class 子类 extends 父类{}
![截屏2022-04-30 11.21.14](https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-04-30%2011.21.14.jpg)
- #### 注意事项和细节:
1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
2. 子类必须调用父类的构造器,完成父类的初始化
3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
4. 指定去调用父类的某个构造器,则显式的调用一下:super(参数列表)
5. super在使用时,必须放在构造器第一行(super只能在构造器中使用)
6. super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7. java所有类都是Object类的子类
8. 父类构造器的调用不限于直接父类!将一直往上追潮直到Object类
9. 子类最多只能继承一个父类(指直接继承),即java中是单继承机制
10. 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
```java public class TopBase { //父类是Object public TopBase() { //super(); Object的无参构造器 System.out.println("构造器TopBase() 被调用...");//1 } } ``` ```java public class Base extends TopBase { //父类 //4个属性 public int n1 = 100; protected int n2 = 200; int n3 = 300; private int n4 = 400; public Base() { //无参构造器 System.out.println("父类Base()构造器被调用...."); } public Base(String name, int age) {//有参构造器 //默认super() System.out.println("父类Base(String name, int age)构造器被调用...."); } public Base(String name) {//有参构造器 System.out.println("父类Base(String name)构造器被调用...."); } //父类提供一个public的方法,返回了n4 public int getN4() { return n4; } public void test100() { System.out.println("test100"); } protected void test200() { System.out.println("test200"); } void test300() { System.out.println("test300"); } private void test400() { System.out.println("test400"); } //call public void callTest400() { test400(); } } ``` ```java /输入ctrl + H 可以看到类的继承关系 public class Sub extends Base { //子类 public Sub(String name, int age) { //1. 调用父类的无参构造器, 如下或者 什么都不写,默认就是调用super() //super();//父类的无参构造器 //2. 调用父类的 Base(String name) 构造器 //super("hsp"); //3. 调用父类的 Base(String name, int age) 构造器 super("king", 20); //细节:super在使用时,必须放在构造器第一行 //细节: super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器 //this() 不能再使用了 System.out.println("子类Sub(String name, int age)构造器被调用...."); } public Sub() {//无参构造器 //super(); //默认调用父类的无参构造器 super("smith", 10); System.out.println("子类Sub()构造器被调用...."); } //当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器 public Sub(String name) { super("tom", 30); //do nothing... System.out.println("子类Sub(String name)构造器被调用...."); } public void sayOk() {//子类方法 //非私有的属性和方法可以在子类直接访问 //但是私有属性和方法不能在子类直接访问 System.out.println(n1 + " " + n2 + " " + n3); test100(); test200(); test300(); //test400();错误 //要通过父类提供公共的方法去访问 System.out.println("n4=" + getN4()); callTest400();// } }
```
- #### 继承本质:
子对象创建完成,建立查找关系
- #### 继承内存图:
![截屏2022-04-30 17.13.52](https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-04-30%2017.13.52.jpg)
## 7.5 多态⭐️⭐️
### 7.5.1多态
- #### 基本介绍:
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的
- #### 多态具体体现
- 方法的多态
1. 重写和重载就是体现多态
- **对象的多态**
1. 一个对象的编译类型和运行类型可以不一致
2. 编译类型在定义对象时,就确定了,不能改变
3. 运行类型是可以变化的
4. **编译类型看定义时 = 号 的左边,运行类型看 = 号的 右边**
- #### 注意事项和细节:
- 向上转型
1. 本质:父类的引用指向了子类的对象
2. 语法:父类类型 引用名 = new 子类类型();
3. 特点:编译类型看左边,运行类型看右边
4. 可以调用父类中的所有成员(需遵守访问权限)
5. 不能调用子类特有成员
6. 最终运行效果看子类的具体实现
- 向下转型
1. 本质:把指向子类对象的父类引用,转成子类对象的子类引用
2. 语法:子类类型 引用名 = (子类类型) 父类引用;
3. 只能强转父类引用,不能强转父类对象
4. 父类的引用必须指向的是当前目标类型的对象
5. 当向下转型后,可以调用子类类型中所有的成员
- 属性
1. 属性没有重写,属性的值看编译类型
2. **instance of 比较较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型**
```java public class PolyDetail { public static void main(String[] args) { //向上转型: 父类的引用指向了子类的对象 //语法:父类类型引用名 = new 子类类型(); Animal animal = new Cat(); Object obj = new Cat();//可以吗? 可以 Object 也是 Cat的父类 //向上转型调用方法的规则如下: //(1)可以调用父类中的所有成员(需遵守访问权限) //(2)但是不能调用子类的特有的成员 //(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的 //animal.catchMouse();错误 //(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法 //,然后调用,规则我前面我们讲的方法调用规则一致。 animal.eat();//猫吃鱼.. animal.run();//跑 animal.show();//hello,你好 animal.sleep();//睡 //老师希望,可以调用Cat的 catchMouse方法 //多态的向下转型 //(1)语法:子类类型 引用名 =(子类类型)父类引用; //问一个问题? cat 的编译类型 Cat,运行类型是 Cat Cat cat = (Cat) animal; cat.catchMouse();//猫抓老鼠 //(2)要求父类的引用必须指向的是当前目标类型的对象 Dog dog = (Dog) animal; //可以吗? System.out.println("ok~~"); } } ``` ```java public class PolyDetail02 { public static void main(String[] args) { //属性没有重写之说!属性的值看编译类型 Base base = new Sub();//向上转型 System.out.println(base.count);// ? 看编译类型 10 Sub sub = new Sub(); System.out.println(sub.count);//? 20 } } class Base { //父类 int count = 10;//属性 } class Sub extends Base {//子类 int count = 20;//属性 } ``` ```java public class PolyDetail03 { public static void main(String[] args) { BB bb = new BB(); System.out.println(bb instanceof BB);// true System.out.println(bb instanceof AA);// true //aa 编译类型 AA, 运行类型是BB //BB是AA子类 AA aa = new BB(); System.out.println(aa instanceof AA); System.out.println(aa instanceof BB); Object obj = new Object(); System.out.println(obj instanceof AA);//false String str = "hello"; //System.out.println(str instanceof AA); System.out.println(str instanceof Object);//true } } class AA {} //父类 class BB extends AA {}//子类 ```
### 7.5.2 动态绑定机制⭐️⭐️
- 当调用对象方法的时候,该方法会和该对象的内存地址/**运行类型**绑定
- 当调用对象属性时,没有动态綁定机制,哪里声明,那里使用()
```java public class DynamicBinding { public static void main(String[] args) { A a = new B(); System.out.println(a.sum()); System.out.println(a.sum1()); } } class A { public int i = 10; public int sum() { return getI() + 10;//当子类不存在sum方法时,会从父类找到sum方法,但动态绑定机制会找到子类的getI返回i值 } public int sum1() { return i + 10; } public int getI() { return i; } } class B extends A { public int i = 20; public int sum() { return i + 20; } public int sum1() { return i + 10; } public int getI() { return i; } }
7.5.3 多态的应用
多态数组
- 多态数组:定义类型为父类类型,里面保存的实际元素类型为子类类型
- 多态参数:方法定义的形参类型为父类类型,实参类型允许为子类类型
7.6 Super
- super代表父类的引用,用于访问父类的属性、方法、构造器
基本介绍:
基本语法:
- 访问父类的属性,但不能访问父类的private属性super.属性名;
- 访问父类的方法,不能访问父类的private方法super.方法名(参数列表);
- 访问父类的构造器(只能放在构造器的第一句,只能出现一句)super(参数列表);
注意事项和细节:
- 调用父类的构造器的好处 (分工明确,父类属性由父类初始化,子类的属性由子类初始化)
- 当子类中有和父类中的成员(属性和方法) 重名时,为了访问父类的成员,必须通过super,如果没有重名,使用super、this、 直接访问效果相同
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则
-
网络异常,图片无法展示|
super和this
实例:
public class Base { //父类是Object public int n1 = 999; public int age = 111; public void cal() { System.out.println("Base类的cal() 方法..."); } public void eat() { System.out.println("Base类的eat()....."); } }
7.7方法重写(overwrite)
- 子类的方法和父类方法一致,那么子类的方法覆盖了父类的方法
基本介绍:
注意事项和细节:
- 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
- 子类方法大于等于父类方法的访问权限
7.8 Object类
==
- 既可以判断基本类型,又可以判断引用类型
- 如果判断基本类型,判断的是值是否相等
- 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
public class Equals01 { public static void main(String[] args) { A a = new A(); A b = a; A c = b; System.out.println(a == c); B bObj = a; System.out.println(bObj == c); } } class A extends B{} class B{}
equals
- equals:是Object类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等
hashCode
- 提高具有哈希结构的容器的效率
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的,不能完全将哈希值等价子地址
- 后面在集合中hashCode 如果需要的话,也会重写
toString
- 默认返回:全类名+@+哈希值的十六进制
- 子类往往重写to String方法,用于返回对象的属性信息
- 重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式
- 当直接输出一个对象时,toString 方法会被默认的调用
finalize
- 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
- 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
- 垃圾回收机制的调用,是由系统来決定(即有自己的GC算法),也可以通过System.gc() 主动触发垃圾回收机制
7.9断点调试
- 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个Bug
基本介绍:
注意事项和细节:
- 在断点调试 过程中,是运行状态,是以对象的运行类型来执行的
- F7:跳入方法内
- F8:逐行执行代码shift+F8:跳出方法
- F9:resume,执行到下一个断点
8 面向对象(高级)
8.1类变量和类方法⭐️
8.1.1类变量
- 类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量
基本介绍:
基本语法:
访问修饰符 static 数据类型 变量名;
static 访问修饰符 数据类型 变量名;
如何访问类变量:
- 类名.类变量名
- 对象名.类变量名
- 静态变量的访问修饰符的访问权限和范围和普通属性是一样的
-
网络异常,图片无法展示|
内存布局:
注意事项和细节:
- 需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)
- 类变量是该类的所有对象共享的,而实例变量是每个对象独享的
- 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
- 类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问
- 实例变量不能通过 类名.类变量名 方式访问
- 类变量是在类加载时就初始化
- 类变量的生命周期是随类的加载开始,随着类消亡而销毁
8.1.2类方法
- 类方法也叫静态方法
基本介绍:
基本语法:
访问修饰符 static 数据返回类型 方法名(){};
static 访问修饰符 数据返回类型 方法名(){};
类方法调用:
- 类名.类方法名
- 对象名.类方法名
- 满足访问修饰符的访向权限和范围
使用场景:
- 当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率
- 程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用
注意事项和细节:
- 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数,普通方法中隐含着this的参数
- 类方法可以通过类名调用,也可以通过对象名调用
- 音通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用
- 类方法中不允许使用和对象有关的关键字,比如this和super
- 静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员
8.2 main方法
深入理解main方法:
- java虛拟机需要调用类的main0方法,所以该方法的访问权限化须是public
- java虚拟机在执行main0方法时不必创建对象,所以该方法心须是static
- 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
- java 执行的程序 参数1 参数2 参数3
注意事项和细节:
- 在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性
- 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的目静态成员
8.3代码块
- 代码化块又称为初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过们包围起来。但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用
基本介绍:
基本语法:
[修饰符]{
代码;
}
注释:
- 修饰符 可选,要写的话,也只能写 static
- 代码块分为两类,使用static 修饰的叫静态代码块,没有static修饰的,叫普通代码块
- 逻辑语句可以为任何逻辑语句
- :号可以写上,也可以省略
注意事项和细节:
- static代码块是类加载时执行,且只会执行一次
- 普通代码块是在创建对象的调用的,创建一次,调用一次
- 类什么时候被加载⭐️⭐️:
- 创建对象实例时
- 创建子类对象实例时,父类也会被加载
- 使用类的静态成员时
- 创建一个对象时,在一个类的调用顺序:
- 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调
- 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,若果有多个普通代码块和stat多个普通属性初始化,则按定义顺序调用)
- 调用构造器
- 构造方法(构造器)的最前面其实隐含了 super()和调用普通代码块
- 创建子类时顺序⭐️⭐️:
- 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 父类构造方法
- 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 子类构造方法
- 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员
8.4单例设计模式
- 就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法单例模式有两种方式:1. 饿汉式 2.懒汉式
基本介绍:
饿汉式:
- 构造器私有化
- 类的内部创建静态对象
- 向外暴露一个静态公共方法
- 特点:在类加载的时候就创建对象实例,可能存在资源浪费
class GirlFriend{ private String name; private static GirlFriend gf = new GirlFriend("小花"); private GirlFriend(String name) { this.name = name; } public static GirlFriend Instance() { return gf; } }
懒汉式:
- 构造器私有化
- 定义一个static静态属性对象
- 提供一个public的static方法,返回一个对象
- 只有当使用(3)的方法时,才返回对象,再次调用时,返回上次创建的对象
- 特点:线程安全问题
class Cat{ private String name; private static Cat cat; private Cat(String name) { this.name = name; } public static Cat getInstance(){ if(cat == null){ cat = new Cat("小花"); } return cat; } }
对比:
- 最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建
- 饿汉式不存在线程安全问题,徽汉式存在线程安全问题
- 饿汉式存在浪费姿源的可能,对象实例都没有使用,那么饿汉式创建的对象就浪费,懒汉式是使用时才创建,就不存在这个问题
8.5 final
基本介绍:
- final可以修饰类、属性、方法、局部变量
- final 类,类不能被继承
- final 方法,子类无法重写方法
- final 属性,属性无法被修改
- final 局部变量,局部变量无法被修改
注意事项和细节:
- final修饰的属性又叫常量,一般用XX_XX_XX 来命名
- final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置:定义时、构造器、代码块
- final修饰的属性是静态的,则初始化的位置只能是 定义时、静态代码块
- final类不能继承,但可以实例化对象
- 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
- 如果一个类已经是final类了,就没有必要再将方法修饰成final方法
- final不能修饰构造器本身
- final 和static 往往搭配使用,效率更高,不会导致类加载-底层编译器做了优化处理
- 包装类(Integer, Double,Float,Boolean等都是final),String也是final类
8.6抽象类
基本介绍:
- 用abstract 关键字来修饰一个类时,这个类就叫抽象类
- 用abstract 关键字来修饰一个方法时,这个方法就是抽象方法,没有方法体
- 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类
注意事项和细节:
- 抽象类不能被实例化
- 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
- 一旦类包含了abstract方法,则这个类必须声明为abstract
- abstract 只能修饰类和方法
- 抽象类可以有任意成员,抽象类本质还是类
- 抽象方法不能有主体
- 如果一个类继承了抽象类,则它必须实现抽象类的所有的抽象方法,除非它自己也声明为abstract类
- 抽象方法不能使用private、final 和static来修饰,因为这些关键字都是和重写相违背的
抽象类模版设计模式
- 编写方法cal(),可以计算某段代码的耗时时间
- 编写抽象方法job()
- 编写一个子类A,继承抽象类Template,井实现job方法
- 编写一个测试类TestTemplate,看看是否好用
public class TextTemplate { public static void main(String[] args) { A a = new A(); a.cal(); B b = new B(); b.cal(); } } abstract public class Template { public abstract void job(); public void cal() { long start = System.currentTimeMillis(); job(); long end = System.currentTimeMillis(); System.out.println("时间" + (end - start)); } } class A extends Template{ public void job() { long num = 0; for (long i = 0; i <= 10000000; i++) { num += i; } } } class B extends Template{ public void job() { long num = 0; for (long i = 0; i <= 10000000; i++) { num *= i; } } }
8.7接口⭐️
- 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来
基本介绍:
基本语法:
interface 接口名{ //属性 //方法 } class 类名 implements 接口 { 自己属性; 自己方法; //必须实现接口的抽象方法 }
- Jdk7.0前 接口里的所有方法都没有方法体(只能抽象方法)
- Jdk8.0后接口类可以有静态方法,defaut实现方法,也就是说接口中可以有方法的具体实现
注意事项和细节:
- 接口不能被实例化
- 接口中所有的方法是 public 和 abstrac 方法,接口中抽象方法,可以不用 abstract 修饰
- 普通类实现接口,就必须将该接口的所有方法实现
- 抽象类实现接口,可以不用实现接口的方法
- 一个类同时可以实现多个接口
- 接口中的属性,只能是 final 的,而且是 public static final 修饰符
- 接口中属性的访问形式:接口名.属性名
- 一个接口不能继承其它的类,但是可以继承多个别的接口
- 接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的
实现接口VS继承类:
- 继承的价值主要在于:解决代码的复用性和可维护性
- 接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法
- 接口比继承更加灵活,继承是满足 is -a的关系,而接口只需满足 like-a的关系
- 接口在一定程度上实现代码解耦
接口类型数组:
public class InterfacePolyArr { public static void main(String[] args) { //多态数组 -> 接口类型数组 Usb[] usbs = new Usb[2]; usbs[0] = new Phone_(); usbs[1] = new Camera_(); /* 给Usb数组中,存放 Phone 和 相机对象,Phone类还有一个特有的方法call(), 请遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外, 还需要调用Phone 特有方法 call */ for(int i = 0; i < usbs.length; i++) { usbs[i].work();//动态绑定.. //和前面一样,我们仍然需要进行类型的向下转型 if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_ ((Phone_) usbs[i]).call(); } } } } interface Usb{ void work(); } class Phone_ implements Usb { public void call() { System.out.println("手机可以打电话..."); } @Override public void work() { System.out.println("手机工作中..."); } } class Camera_ implements Usb { @Override public void work() { System.out.println("相机工作中..."); } }
8.8内部类⭐️
基本介绍:
- 一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)
- 内部类最大的特点就是可以直接访问私有属性,井且可以体现类与类之间的包含关系
基本语法:
class Outer{//外部类 class Inner{//内部类 } } class Other{//外部其他类 }
内部类分类:
- 定义在外部类局部位置上:1局部类内部(有类名) 2.匿名局部类(没有类名⭐️)
- 定义在外部类的成员位置上:1.成员内部类(无static修饰) 2.静态内部类(static修饰)
8.8.1局部内部类
- 局部内部类是定义在外部类的局部位置,在方法中、代码块中,并且有类名
基本介绍:
注意事项和细节:
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,但是可以使用final修饰
- 作用域:仅仅在定义它的方法或代码块中
- 内部类访问外部类:直接访问
- 外部类访问内部类:创建对象再访问
- 外部其他类访问局部内部类:不能访问
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果访问外部类的成员使用"外部类名.this.成员"去访问
public class LocalInnerClass {// public static void main(String[] args) { //演示一遍 Outer02 outer02 = new Outer02(); outer02.m1(); System.out.println("outer02的hashcode=" + outer02); } } class Outer02 {//外部类 private int n1 = 100; private void m2() { System.out.println("Outer02 m2()"); }//私有方法 public void m1() {//方法 //1.局部内部类是定义在外部类的局部位置,通常在方法 //3.不能添加访问修饰符,但是可以使用final 修饰 //4.作用域 : 仅仅在定义它的方法或代码块中 final class Inner02 {//局部内部类(本质仍然是一个类) //2.可以直接访问外部类的所有成员,包含私有的 private int n1 = 800; public void f1() { //5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2() //7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员, // 使用 外部类名.this.成员)去访问 // Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this就是哪个对象 System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1); System.out.println("Outer02.this hashcode=" + Outer02.this); m2(); } } //6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可 Inner02 inner02 = new Inner02(); inner02.f1(); } }
8.8.2匿名内部类⭐️⭐️
- 匿名内部类是定义在外部类的局部位置,比如方法中,并目没有类名,同时还是一个对象
基本介绍:
基本语法:
new 类或接口(参数列表){
类体;
};
底层:
- 本质: IA tiger = class Xxxx$1 class Xxxx$1 = new IA
- 接口的底层:class Xxxx$1 implement IA{@Overide}
- 本质: Father father = class Xxxx$2 class Xxxx$2 = new Father
- 类的底层:class Xxxx$2 extends Father{@Overide}
注意事项和细节:
- 匿名内部类既是一个类的定义同时本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量
- 作用域:仅仅在定义它的方法或代码块中
- 匿名内部类方位外部类成员:直接访问
- 外部其他类访问匿名内部类:不能访问
- 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
public class AnonymousInnerClass { public static void main(String[] args) { Outer04 outer04 = new Outer04(); outer04.method(); } } class Outer04 { //外部类 private int n1 = 10;//属性 public void method() {//方法 //基于接口的匿名内部类 // //1.需求: 想使用IA接口,并创建对象 //2.传统方式,是写一个类,实现该接口,并创建对象 //3.需求是 Tiger/Dog 类只是使用一次,后面再不使用 //4. 可以使用匿名内部类来简化开发 //5. tiger的编译类型 ? IA //6. tiger的运行类型 ? 就是匿名内部类 Outer04$1 /* 我们看底层 会分配 类名 Outer04$1 class Outer04$1 implements IA { @Override public void cry() { System.out.println("老虎叫唤..."); } } */ //7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址 // 返回给 tiger //8. 匿名内部类使用一次,就不能再使用 IA tiger = new IA() { @Override public void cry() { System.out.println("老虎叫唤..."); } }; System.out.println("tiger的运行类型=" + tiger.getClass()); tiger.cry(); // IA tiger = new Tiger(); // tiger.cry(); //演示基于类的匿名内部类 //分析 //1. father编译类型 Father //2. father运行类型 Outer04$2 //3. 底层会创建匿名内部类 /* class Outer04$2 extends Father{ @Override public void test() { System.out.println("匿名内部类重写了test方法"); } } */ //4. 同时也直接返回了 匿名内部类 Outer04$2的对象 //5. 注意("jack") 参数列表会传递给 构造器 Father father = new Father("jack"){ @Override public void test() { System.out.println("匿名内部类重写了test方法"); } }; System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2 father.test(); //基于抽象类的匿名内部类 Animal animal = new Animal(){ @Override void eat() { System.out.println("小狗吃骨头..."); } }; animal.eat(); } } interface IA {//接口 public void cry(); } //class Tiger implements IA { // // @Override // public void cry() { // System.out.println("老虎叫唤..."); // } //} //class Dog implements IA{ // @Override // public void cry() { // System.out.println("小狗汪汪..."); // } //} class Father {//类 public Father(String name) {//构造器 System.out.println("接收到name=" + name); } public void test() {//方法 } } abstract class Animal { //抽象类 abstract void eat(); }
8.8.3成员内部类
- 成员内部类是定义在外部类的成员位置,并且没有static修饰
基本介绍:
注意事项和细节:
- 可以直接访问外部类的所有成员,包含私有的
- 可以添加任意访问修饰符
- 作用域:为整个外部类类体
- 成员内部类访问外部类:直接访问
- 外部类访问成员内部类;创建对象,再访问
- 外部其他类访问成员内部类:
- 外部类.内部类 引用名 = 外部对象.new 内部类();
- 外部类.内部类 引用名 = 外部对象.get();
- 如果外部类和成员内部类的成员重名时,成员内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
public class MemberInnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08(); outer08.t1(); //外部其他类,使用成员内部类的三种方式 // // 第一种方式 // outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员 // 这就是一个语法,不要特别的纠结. Outer08.Inner08 inner08 = outer08.new Inner08(); inner08.say(); // 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象 Outer08.Inner08 inner08Instance = outer08.getInner08Instance(); inner08Instance.say(); } } class Outer08 { //外部类 private int n1 = 10; public String name = "张三"; private void hi() { System.out.println("hi()方法..."); } //1.注意: 成员内部类,是定义在外部内的成员位置上 //2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员 public class Inner08 {//成员内部类 private double sal = 99.8; private int n1 = 66; public void say() { //可以直接访问外部类的所有成员,包含私有的 //如果成员内部类的成员和外部类的成员重名,会遵守就近原则. //,可以通过 外部类名.this.属性 来访问外部类的成员 System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + Outer08.this.n1); hi(); } } //方法,返回一个Inner08实例 public Inner08 getInner08Instance() { return new Inner08(); } //写方法 public void t1() { //使用成员内部类 //创建成员内部类的对象,然后使用相关的方法 Inner08 inner08 = new Inner08(); inner08.say(); System.out.println(inner08.sal); } }
8.8.4静态内部类
- 静态内部类是定义在外部类的成员位置,并且有static修饰
基本介绍:
注意事项和细节:
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符
- 作用域:整个外部类类体
- 静态内部类访问外部类:直接访问
- 外部类访问静态内部类:创建对象,再访问
- 外部其他类访问静态内部类:
- 外部类.内部类 引用名 = new 外部类.内部类();
- 外部类.内部类 引用名 = 外部对象.get();
- 外部类.内部类 引用名 = 外部类.get();
- 如果外部类和静态内部类的成员重名时,静态内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
public class StaticInnerClass01 { public static void main(String[] args) { Outer10 outer10 = new Outer10(); outer10.m1(); //外部其他类 使用静态内部类 //方式1 //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限) Outer10.Inner10 inner10 = new Outer10.Inner10(); inner10.say(); //方式2 //编写一个方法,可以返回静态内部类的对象实例. Outer10.Inner10 inner101 = outer10.getInner10(); System.out.println("============"); inner101.say(); Outer10.Inner10 inner10_ = Outer10.getInner10_(); System.out.println("************"); inner10_.say(); } } class Outer10 { //外部类 private int n1 = 10; private static String name = "张三"; private static void cry() {} //Inner10就是静态内部类 //1. 放在外部类的成员位置 //2. 使用static 修饰 //3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员 //4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员 //5. 作用域 :同其他的成员,为整个类体 static class Inner10 { private static String name = "韩顺平教育"; public void say() { //如果外部类和静态内部类的成员重名时,静态内部类访问的时, //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员) System.out.println(name + " 外部类name= " + Outer10.name); cry(); } } public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问 Inner10 inner10 = new Inner10(); inner10.say(); } public Inner10 getInner10() { return new Inner10(); } public static Inner10 getInner10_() { return new Inner10(); } }
8.9类加载顺序⭐️⭐️
类什么时候被加载:
- 创建对象实例时
- 创建子类对象实例时,父类也会被加载
- 使用类的静态成员时
创建一个对象时,在一个类的调用顺序:
- 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调
- 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,若果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
- 调用构造方法
创建子类时顺序:
- 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 父类构造方法
- 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 子类构造方法
9 枚举和注解
- 枚举是一组常量的集合,属于一种特殊的类,里面只包含一组有限的特定的对象
基本介绍:
9.1自定义类实现枚举
实现步骤:
- 构造器私有化
- 本类内部创建一组对象
- 对外暴露对象(通过为对象添加public final static修饰符)
- 提供get方法,但是不提供set方法
注意事项和细节:
- 不需要提供setxxx 方法,因为枚举对象值通常为只读
- 枚举对象/属性使用 final + static 共同修饰,实现底层优化
- 枚举对象名通常使用全部大写,常量的命名规范
- 枚举对象根据需要,也可以有多个属性
9.2enum关键字实现枚举
实现步骤:
- 关键字 enum 代替 class
- 行首:常量名(实参列表)
enum Season { SPRING("春天","温暖"),WINTER("冬天","寒冷"); private String name; private String desc; private Season(String name, String desc) { this.name = name; this.desc = desc; }
注意事项和细节:
- 使用enum 关键字开发一个枚举类时,默认会继承Enum类
- 传统的 public static final Season SPRING = new Season("春天”"温暖");简化成 SPRING("春天”,"温暖”),我们必须知道它调用的是哪个构造器
- 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
- 多个枚举对象,使用,间隔,最后;结尾
- 枚举对象必须放在枚举类行首
常用方法:
- toString:Enum类己经重写过了,返回的是当前对象名子类可以重写该方法,用于返回对象的属性信息
- name:返回当前对象名(常量名),子类中不能重写
- ordinal:返回当前对象的位置号,默认从0开始
- values:返回当前枚举类中所有的常量
- valueof:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常
- compareTo:比较两个枚举常量的编号,做差
public class EnumMethod { public static void main(String[] args) { //使用Season2 枚举类,来演示各种方法 Season2 autumn = Season2.AUTUMN; //输出枚举对象的名字 System.out.println(autumn.name()); //ordinal() 输出的是该枚举对象的次序/编号,从0开始编号 //AUTUMN 枚举对象是第三个,因此输出 2 System.out.println(autumn.ordinal()); //从反编译可以看出 values方法,返回 Season2[] //含有定义的所有枚举对象 Season2[] values = Season2.values(); System.out.println("===遍历取出枚举对象(增强for)===="); for (Season2 season: values) {//增强for循环 System.out.println(season); } //valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常 //执行流程 //1. 根据你输入的 "AUTUMN" 到 Season2的枚举对象去查找 //2. 如果找到了,就返回,如果没有找到,就报错 Season2 autumn1 = Season2.valueOf("AUTUMN"); System.out.println("autumn1=" + autumn1); System.out.println(autumn == autumn1); //compareTo:比较两个枚举常量,比较的就是编号 // //1. 就是把 Season2.AUTUMN 枚举对象的编号 和 Season2.SUMMER枚举对象的编号比较 //2. 看看结果 /* public final int compareTo(E o) { return self.ordinal - other.ordinal; } Season2.AUTUMN的编号[2] - Season2.SUMMER的编号[3] */ System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER)); //补充了一个增强for // int[] nums = {1, 2, 9}; // //普通的for循环 // System.out.println("=====普通的for====="); // for (int i = 0; i < nums.length; i++) { // System.out.println(nums[i]); // } // System.out.println("=====增强的for====="); // //执行流程是 依次从nums数组中取出数据,赋给i, 如果取出完毕,则退出for // for(int i : nums) { // System.out.println("i=" + i); // } } }
实现接口:
- 使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum,而Java是单继承机制
- 枚举类和普通类一样,可以实现接口:enum 类名 implements 接口1,接口2{}