JavaSE笔记--韩顺平(入门详细版)(一)

简介: JavaSE笔记--韩顺平(入门详细版)(一)

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";



注意事项和细节:

  1. 变量表示内存中的一个存储区域[不同的变量,类型不同,占用的空间大小不同]
  2. 该区域有自己的名称[变量名]和类型[数据类型]
  3. 变量必须先声明,后使用,即有顺序
  4. 该区域的数据可以在同一类型范围内不断变化
  5. 变量在同一个作用域内不能重名
  6. 变量=变量名+值+数据类型


2.2 基本数据类型⭐️


网络异常,图片无法展示
|


2.2.1 数值型



整数类型  (byte [1]  short[2]  int[4]  long [8])

  1. Java的整型常量(具体值)默认为 int 型,声明long型常量须后加‘l’或‘L’
  2. java程序中变量常声明为int型,除非不足以表示大数,才使用long
  3. bit: 计算机中的最小存储单位,byte:t算机中基本存储单元,1byte = 8 bit

浮点(小数)类型 (float [4]  double [8])

  1. 浮点数=符号位+指数位+尾数位
  2. 尾部可能丢失,造成精度损失
  3. Java 的浮点型常量(具体值)默认为double型,声明float型常量,须后加'f'或'F'
  4. 十进制数形式:5.12    512.0f    .512(必须有小数点)科学计数法形式:5.12e2    5.12E-2
  5. 通常情况默认使用double

字符型(Char[2])

  1. 使用单引号表示
  2. Java中还允许使用转义字符来将其后的字符转变为特殊字符型常量
  3. char的本质是一个整数,输出时是unicode码对应字符
  4. char类可以进行运算

布尔型(boolean[1])

  1. boolean类型数据只允许取值true和false,无null
  2. boolean类型占1个字节
  3. 不可以用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

转换规则:


注意事项和细节


  1. 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,再进行计算
  2. 我们把精度(容量)大的数据类型赋值给精度(容量)小 的数据类型时,就会报错,反之就会进行自动类型转换。
  3. byte    short    char    之间不会相互转换
  4. byte    short    char    计算是首先转换为int类型
  5. boolean不参与转换
  6. 自动提升原则:表达式结果的类型自动提升为操作数中最大的类型


2.3.2 强制类型转换


  • 自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符(),但可能造成精度降低或溢出,格外要注意

基本介绍:

int i = (int)8.8;
System.out.println(i);



注意事项和细节


  1. 当数据从精度  大——>小,就需要使用到强制转换
  2. 强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
int y = int(10*3.5+6*1.5)


  1. char类型可以保存 int的常量值,但不能保存int的变量值,需要强转
int m = 100;
char c2 = m;//false
char c3 = (char)m;//ture
  1. 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+号使用


  1. 当左右两边都是数值型时,则做加法运算
  2. 当左右两边有一方为字符串,则做拼接运算
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 赋值运算符


  • 赋值运算符就是将某个运算后的值,赋给指定的变量

基本介绍:


注意事项和细节:

  1. 运算顺序从右往左
  2. 赋值运算符的左边只能是变量,右边可以是变量、表达式、常量值
  3. 复合赋值: a  x=  b  等于  a  =  a  x  b
  4. 复合赋值运算符会进行类型转换

byte b = 3;

b += 2;  //等价于 b = (byte)(b + 2)

b++;  //等价于 b = (byte)(b + 1)



3.5 三元运算符

基本语法:

运算规则:

  1. 如果条件表达式为true,运算后的结果是表达式1;
  2. 如果条件表达式为false,运算后的结果是表达式2;

注意事项和细节:

  1. 表达式1和表达式2要为可以赋给接收变量的类型(或可以自动转换)
  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 运算符优先级

优先级:

  1. () {} , 等
  2. 单目运算符 ++ --
  3. 算术运算符
  4. 位移运算符
  5. 比较运算符
  6. 逻辑运算符
  7. 三元运算符
  8. 赋值运算符

3.6 标识符的命名规则和规范

规则:

  1. 由26个英文字母大小写,0~9,或$组成
  2. 数字不可开头
  3. 不可以使用关键字和保留宇,但能包含关键字和保留字
  4. Java中严格区分大小写,长度无限制
  5. 标识符不能包含空格

规范:

  1. 包名:多单词组成时所有字母都小写:aaa.bbb.ccc
  2. 类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
  3. 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单河开始每个单词首字母大写:xxxYyyzzz
  4. 常量名:所有字母都大写,多单词时每个单词用下划线连接:XXX_YYY_ZZZ

3.7 关键字、保留字

  • 被Java语言赋子了特殊含义,用做专门用途的字符串(单词)特点:关键字中所有字母都为小写

关键字基本介绍:

  • 现有Java版本尚未使用,但以后版本可能会作为关键字使用。自己命名标识符时要避免使用这些保留宇byValue、cast、future、 generic、 inner、 operator、outer、rest、var、goto、const

保留字基本介绍:

3.8 键盘输入

  • 在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取lnput.java,需要一个扫描器(对象),就是 Scanner

基本介绍:

步骤:

  1. 导入该类的包,java.util.*
  2. 创建该类的对象(声明变量)
  3. 调用里面的功能
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 原码 反码 补码⭐️⭐️

运算规则:

  1. 二进制的最高位是符号位:0表示正数,1表示负数
  2. 正数的原码、反码、补码都一样(三码合一)
  3. 负数的反码 = 它的原码符号位不变,其它位取反
  4. 负数的补码 = 它的反码+1,负数的反码 = 负数的补码 - 1
  5. 0的反码,补码都是0
  6. java中的数都是有符号的
  7. 计算机运算的时候,都是以补码的方式来运算的
  8. 看运算结果的时候,要看他的原码

3.11 位运算符⭐️

位运算符

  • 按位与 &
  • 按位或 |
  • 按位异或 ^
  • 按位取反 ~
  • 算数右移 >> 低位溢出,符号位不变,并用符号位补溢出的高位(本质/2)
  • 算数左移 << 符号位不变,低位补0(本质*2)
  • 逻辑右移 >>> 低位溢出,高位补0

位运算过程:

  1. 用原码得到补码
  2. 用补码进行相关逻辑运算
  3. 运算完毕将补码转换成原码


4.2 分支控制(if  else  switch)


4.2.1 单分支



基本语法:

if(条件表达式){
  执行代码块;
}

注意事项和细节:

  1. 当条件表达式为ture 时,就会执行{}的代码。如果为false,就不执行
  2. 如果{}中只有一条语句,则可以不用{},建议写上{}


4.2.2 双分支



基本语法:

if(条件表达式){
  执行代码块;
}else{
  执行代码块2;
}



注意事项和细节:

  1. 当条件表达式成立,即执行代码块1,否则执行代码块2
  2. 如果执行代码块有一条语句,则{}可以省略,否则,不能省略


4.2.3 多分支



基本语法:

if(条件表达式){
  执行代码块;
}else if(条件表达式2){
  执行代码块2;
}
...
else{
  执行代码块n;
}

注意事项和细节:

  1. 当条件表达式1成立时,即执行代码块1
  2. 如果表达式1不成立,才去判断表达式2是否成立
  3. 如果表达式2成立,就执行代码块2
  4. 以此类推,如果所有的表达式都不成立则执行else的代码块
  5. 多分支可以没有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;
}

说明:

  1. switch 关键字,表示swtich分支
  2. 表达式对应一个值
  3. case 常量1:当表达式的值等于常量1,就执行语句块1
  4. break :表示退出swtich
  5. 如果和 case 常量1匹配,就执行语句块1,如果没有匹配,就继续匹配 case 常量2
  6. 如果一个都没有匹配上,执行default

注意事项和细节:

  1. 表达式数据类型,应和case 后的常量类型一致,或者是可以自动转成可以相互比较的类型,比如输入的是字符,而常量是 int
  2. switch(表达式)中表达式的返回值必须是:(byte,short.int,char,enum,String)
  3. case子句中的值必须是常量,而不能是变量
  4. default子句是可选的,当没有匹配的case时,执行default
  5. break语句跳出switch语句块,如果没有break,程序会执行到结尾


4.3 循环控制(for  while  do while)⭐️


4.3.1  for循环控制



基本语法:

for(循环变量初始化;循环条件;循环变量迭代){

 循环操作语句;

}



说明:

  1. 四要素:(1)循环变量初始化(2)循环条件(3)循环操作(4)循环变量迭代
  2. 循环操作,这里可以有多条语句,也就是我们要循环执行的代码
  3. 如果 循环操作(语句) 只有一条语句,可以省路{},建议不要省略

注意事项和细节:

  1. 循环条件是返回一个布尔值的表达式
  2. for(;循环判断条件;)中的初始化和变量迭代可以写到其它地方,但是两边的分号不能省略
  3. 循环初始值可以有多条初始化语句,但要求类型一样,井且中间用逗号隔开
  4. 循环变量迭代也司以有名条变量迭代语句,中间用逗号隔开


4.3.2  while循环控制



基本语法

循环变量初始化;

while(循环条件){

 循环体;

 循环变量迭代;

}



说明:

  1. 四要素:(1)循环变量初始化(2)循环条件(3)循环操作(4)循环变量迭代
  2. 四要素位置不同

注意事项和细节:

  1. 循环条件是返回一个布尔值的表达式
  2. while循环是先判断在执行语句


4.3.3  do while循环控制



基本语法

循环变量初始化;

do{

 循环体;

 循环变量迭代;

}while(循环条件);



说明:

  1. 四要素:(1)循环变量初始化(2)循环条件(3)循环操作(4)循环变量迭代
  2. 四要素位置不同
  3. 先执行在判断,至少执行一次
  4. 最后有一个;

注意事项和细节:

  1. 循环条件是返回一个布尔值的表达式
  2. 先执行在判断,至少执行一次


4.3.4 多重循环控制



介绍:

  1. 将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for,while,do.while均可以作为外层循环和内层循环
  2. 嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为false时,才会完全跳出内层盾环,才可结束外层的当次循环,开始下一次的循环
  3. 设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次


4.4 跳转控制break


  • break语句用于终止某个语句块的执行,一般使用在switch或者循环[for,while,do while]

基本介绍:


注意事项和细节:

  1. break语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块
  2. 标签的基本使用

label1:{......

 label2:{

  break label1;

 }  

}



4.5 跳转控制continue



基本介绍:

  • continue语句用于结束本次循环,继续执行下一次循环
  • continue语句出现在多层嵌套的福环语句体中时,可以通过标签指明要跳过的是哪一层循环

注意事项和细节:

  1. continue语句出现在多层嵌套的语句块中时,可以通过标签指明要终止的是哪一层语句块
  2. 标签的基本使用
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-静态初始化


  • 数据类型 数组名[] = {元素值,元素值......}

语法:


注意事项和细节


  1. 数组是多个相同类型数据的组合
  2. 数组中的元素可以是任何数据类型,包括基本类型和引用类型,但是不能混用
  3. 数组创建后如果没有赋值,有默认值:int0,short 0, byte 0, long 0, float 0.0,double 0.0char \u0000,boolean false, String null
  4. 使用数组的步骤 1. 声明数组并开辟空间 2给数组各个元素赋值 3 使用数组
  5. 数组的下标是从0开始的
  6. 数组下标必须在指定范围内使用,否则报:下标越界异常
  7. 数组属引用类型,数组型数据是对象(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();
        }
    }
}


  • 网络异常,图片无法展示
    |

二维数组内存图


注意事项和细节:

  1. 二维数组元素个数:arr.length
  2. 取出一维元素需要遍历两次
  3. 访问第(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}}

语法:


注意事项和细节:


  1. 一维数组声明方式:int [] x 或者 int x []
  2. 二维数组声明方式:int [] [] y 或者 int[] y [] 或者 int y [] []
  3. 二维数组实际上是由多个一维数组组成的,它的各个一维数组的长度可以相同,也可以不相同


5.3 排序(基础)


5.3.1 排序分类


  1. 想需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择式排序法和插入式排序法)
  2. 数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)


5.3.2 冒泡排序


  • 冒泡排序 (Bubble Sorting)的基本思想是:通过对待排序序列从后向前(从下标较大的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒

定义:


冒泡排序特点

  1. 一共有n个元素
  2. 一共进行了n-1轮排序,可以看成是外层循
  3. 每1轮排序可以确定一个数的位置,比如第1轮排序确定最大数,第2轮排序,确定第2大的数位置,依次类推
  4. 当进行比较时,如果前面的数大于后面的数,就交换

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 就是属性

注意事项和细节:

  1. 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名
  2. 访问修饰符:控制属性和访问范围,有四种访问修饰符 public,proctected,defaul,private
  3. 属性的定义类型可以为任意类型,包含基本类型或引用类型
  4. 属性如果不赋值,有默认值,规则和数组一致:
    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();



对象创建流程分析⭐️⭐️

  1. 加载Person类信息(就是Person.class 且只加载一次)
  2. 在堆中分配空间(地址)
  3. 完成对象初始化
    (3.1默认初始化 age = 0 name =null  
    3.2显示初始化 age = 90 name = null
    3.3构造器初始化 age = 20 name = 小倩)
  4. 在对象在堆中的地址返回给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内存结构分析:

  1. 栈:一般存放基本数据类型(局部变量)
  2. 堆:存放对象(Cat cat,数组等)
  3. 方法区:常量池(常量,比如字符串),类加载信息

Java创建对象流程

  1. 先加载Person类信息(属性和方法信息,只会加载一次)
  2. 在堆中分配空间,进行默认初始化(看规则)
  3. 把地址赋给 p,p就指向对象
  4. 行指定初始化,比如 p.name =" jack"  p.age = 10
Person p = new Person();
p.name = "jack";
p.age = 10;



6.2 成员方法⭐️



方法定义:

  1. 访问修饰符 返回数据类型 方法名(形参列表)  {//方法体            语句;            return 返回值;}
  1. 形参列表:表示成员方法输入 callint n)
  2. 数据类型(返回类型):表示成员方法输出,void 表示没有返回值
  3. 方法主体:表示为了实现某一功能代码块
  4. return 语句不是必须的


6.2.1 调用方法:


  1. public 表示方法是公开
  2. void:表示方法没有返回值
  3. speak() :speak是方法名,()形参列表
  4. {}方法体,可以写我们要执行的代码
  5. System.out.println(”我是一个好人”);表示我们的方法就是输出一句话
  6. 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 调用的内存机制:



调用的内存机制:


  1. public 表示方法是公开的
  2. int :表示方法执行后,返回一个 int 值
  3. getsum 方法名
  4. (int num1, int num2) 形参列表,2个形参,可以接收用户传入的两个数
  5. return res;表示把res 的值,返回
public int getSum(int num1,int num2){
        int res = num1 + num2;
        return res;

网络异常,图片无法展示
|


6.2.3 注意事项和细节



访问修饰符:

  1. 作用是控制方法使用的适用范围,若果不写则默认访问

返回类型:

  1. 一个方法最多有一个返回值
  2. 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
  3. 方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值;要求返回值类型必须和return的值类型一致或兼容
  4. 如果方法是void,则方法体中可以没有return语句,或者 只写 return

方法名:

  1. 方法名遵循驼峰法则

参数列表:

  1. 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,比如 getSum(int n1,int n2)
  2. 参数类型可以为任意类型,包含基本类型或引用类型,比如 printArr(intlIl map)
  3. 调用参数的方法时,一定对应着参数列表传入相同类型或莱容类型 的参数
  4. 方法定义时的参数称为形式参数,简称形参;方法调用时的参数称为实际参数,简称实参,实参和形参的类型要一致或兼容、个数、顺序必须一致

方法体:

  1. 里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用
  2. 里面不能再定义方法!即:方法不能嵌套定义

方法调用细节:

  1. 同一个类中的方法调用:直接调用即可
  2. 跨类中的方法A类调用B类方法:需要通过对象名调用
  3. 跨类的方法调用和方法的访问修饰符相关


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;
        }
    }
}

网络异常,图片无法展示
|


注意事项和细节:

  1. 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
  2. 方法的局部变量是独立的,不会相互影响,比如n变量
  3. 如果方法中使用的是引用类型变量(比如数组、对象),就会共享该引用类型的数据
  4. 递归必须向退出递归的条件逼近,否则就是无限递归
  5. 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕,栈空间回收


6.5 方法重载 (overload)


  • java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致

基本介绍:


注意事项和细节

  1. 方法名:必须相同
  2. 形参列表:必须不同(参数类型、个数、顺序)
  3. 返回类型:无要求


6.6 可变参数


  • java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法,通过可变参数实现

基本概念:


基本语法:

访问修饰符 返回类型 方法名(**数据类型...** 形参名){}



注意事项和细节:

  1. 可变参数的实参可以为0个或任意多个
  2. 可变参数的实参可以为数组
  3. 可变参数的本质就是数组
  4. 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
  5. 一个形参列表中只能出现一个可变参数


6.7 变量作用域⭐️



全局变量和局部变量:

  1. 主要的变量就是属性(成员变量)和局部变量
  2. 局部变量一般是指在成员方法中定义的变量
  3. 全局变量:也就是属性,作用域为整个类体
  4. 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中
  5. 全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值

注意事项和细节:

  1. 全局变量和局部变量可以重名,访问时遵循就近原则
  2. 在同一个作用域中,两个局部变量,不能重名
  3. 全局变量生命周期长,伴随着对象的创建而创建,伴随着对象的销毁而销毁
  4. 局部变量生命周期短,伴随着它的代码块的执行而创建,伴随着代码块的结束而结束:即在一次方法调用过程中
  5. 作用域范围不同全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)局部变量:只能在本类中对应的方法中使用
  6. 修饰符不同全局变量/属性可以加修饰符局部变量不可以加修饰符

两种调用方式

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),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化

基本介绍:


基本语法:

[修饰符] 方法名 (行参列表){

 方法体;

}



注意事项和细节:

  1. 修饰符可以默认
  2. 一个类可以定义多个不同的构造器,即构造器重载
  3. 构造器名和类名要相同
  4. 构造器没有返回值
  5. 构造器是完成对象的初始化,井不是创建对象
  6. 在创建对象时,系统自动的调用该类的构造方法
  7. 如果没有定义构造器,系统会自动给类生成一个默认无参构造器(默认构造器)
  8. 定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下
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,代表当前对象

基本介绍:

  • 网络异常,图片无法展示
    |

内存分析:


注意事项和细节:

  1. this关键字可以用来访问本类的属性、方法、构造器
  2. this用于区分当前类的属性和局部变量
  3. 访问成员方法的语法:this.方法名(参数列表);
  4. 访问构造器语法:this(参数列表):注意只能在构造器中使用(在构造器中访问另外一个构造器,必须放在第一条语句)
  5. 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



常用快捷键:

  1. command + d  删除当前行
  2. command + option + 向下光标  向下复制当前行
  3. option + /  补全代码
  4. command + /  注释
  5. option + enter  导入该行的类
  6. command + option + L  格式化代码
  7. control + R  运行
  8. optio + A  构造器
  9. control + H 查看继承关系
  10. command + B  定位方法
  11. .var  自动变量名
  12. Command + option + T 环绕方式

常用模版快捷键

  1. fori  遍历
  2. itar  遍历数组
  3. iter  增强遍历
  4. sout  打印换行


7.1 包


  • Package 包名

基本语法:


包的命名:

  • 只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字

命名规范:

  • 小写字母+小圆点一般是  com.公司名.项目名.业务模块名

引入包:

  1. import java.uti Scanner;就只是引入一个类Scanner
  2. import java.util*:1/ 表示将java.util 包所有都引入

注意事项和细节:

  1. package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一个package
  2. import指令 位置放在package的下面,在类定义前面,可以有多句目没有顺序要求


7.2 访问修饰符


  • java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围)

基本介绍:


访问权限⭐️⭐️:

  1. 公开级别:用public修饰,对外公开
  2. 受保护级别:用protected修饰,对子类和同一个包中的类公开
  3. 默认级别:没有修饰符号,向同一个包的类公开
  4. 私有级别:用private修饰,只有类本身可以访问,不对外公开

访问级别

访问修饰符

同类

同包

子类

不同包

公开

public

受保护

protected

默认

私有

private


注意事项和细节:

  1. 修饰符可以用来修饰类中的属性,成员方法以及类
  2. 只有默认的和public才能修饰类,并目遵循上述访问权限的特点
  3. 子类待定
  4. 成员方法的访问规则和属性完全样


7.3 封装⭐️


  • 封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作方法封装在一起数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作

基本介绍:


封装步骤:

  1. 将属性进行私有化private【不能直接修改属性】
  2. 提供一个公共的(public)set方法,用于对属性判断井赋值public void setxxx(类型 参数名){加入数据验证的业务逻辑;属性 = 参数名;}
  3. 提供个公共的(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 多态的应用



多态数组

  1. 多态数组:定义类型为父类类型,里面保存的实际元素类型为子类类型
  2. 多态参数:方法定义的形参类型为父类类型,实参类型允许为子类类型


7.6  Super


  • super代表父类的引用,用于访问父类的属性、方法、构造器

基本介绍:


基本语法:

  1. 访问父类的属性,但不能访问父类的private属性super.属性名;
  2. 访问父类的方法,不能访问父类的private方法super.方法名(参数列表);
  3. 访问父类的构造器(只能放在构造器的第一句,只能出现一句)super(参数列表);

注意事项和细节:

  1. 调用父类的构造器的好处 (分工明确,父类属性由父类初始化,子类的属性由子类初始化)
  2. 当子类中有和父类中的成员(属性和方法) 重名时,为了访问父类的成员,必须通过super,如果没有重名,使用super、this、 直接访问效果相同
  3. 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)


  • 子类的方法和父类方法一致,那么子类的方法覆盖了父类的方法

基本介绍:


注意事项和细节:

  1. 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样
  2. 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
  3. 子类方法大于等于父类方法的访问权限


7.8  Object类



==

  1. 既可以判断基本类型,又可以判断引用类型
  2. 如果判断基本类型,判断的是值是否相等
  3. 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
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

  1. equals:是Object类中的方法,只能判断引用类型
  2. 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等

hashCode

  1. 提高具有哈希结构的容器的效率
  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的
  4. 哈希值主要根据地址号来的,不能完全将哈希值等价子地址
  5. 后面在集合中hashCode 如果需要的话,也会重写

toString

  1. 默认返回:全类名+@+哈希值的十六进制
  2. 子类往往重写to String方法,用于返回对象的属性信息
  3. 重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式
  4. 当直接输出一个对象时,toString 方法会被默认的调用

finalize

  1. 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
  2. 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
  3. 垃圾回收机制的调用,是由系统来決定(即有自己的GC算法),也可以通过System.gc() 主动触发垃圾回收机制


7.9断点调试


  • 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个Bug

基本介绍:


注意事项和细节:

  1. 在断点调试 过程中,是运行状态,是以对象的运行类型来执行的
  2. F7:跳入方法内
  3. F8:逐行执行代码shift+F8:跳出方法
  4. F9:resume,执行到下一个断点


8 面向对象(高级)


8.1类变量和类方法⭐️


8.1.1类变量


  • 类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量

基本介绍:


基本语法:

访问修饰符 static 数据类型 变量名;

static 访问修饰符 数据类型 变量名;



如何访问类变量:

  1. 类名.类变量名
  2. 对象名.类变量名
  3. 静态变量的访问修饰符的访问权限和范围和普通属性是一样的
  • 网络异常,图片无法展示
    |

内存布局:


注意事项和细节:

  1. 需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)
  2. 类变量是该类的所有对象共享的,而实例变量是每个对象独享的
  3. 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
  4. 类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问
  5. 实例变量不能通过 类名.类变量名 方式访问
  6. 类变量是在类加载时就初始化
  7. 类变量的生命周期是随类的加载开始,随着类消亡而销毁


8.1.2类方法


  • 类方法也叫静态方法

基本介绍:


基本语法:

访问修饰符 static 数据返回类型 方法名(){};

static 访问修饰符 数据返回类型 方法名(){};



类方法调用:

  1. 类名.类方法名
  2. 对象名.类方法名
  3. 满足访问修饰符的访向权限和范围

使用场景:

  1. 当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率
  2. 程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用

注意事项和细节:

  1. 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数,普通方法中隐含着this的参数
  2. 类方法可以通过类名调用,也可以通过对象名调用
  3. 音通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用
  4. 类方法中不允许使用和对象有关的关键字,比如this和super
  5. 静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员


8.2  main方法



深入理解main方法:

  1. java虛拟机需要调用类的main0方法,所以该方法的访问权限化须是public
  2. java虚拟机在执行main0方法时不必创建对象,所以该方法心须是static
  3. 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
  4. java 执行的程序 参数1 参数2 参数3


网络异常,图片无法展示
|



注意事项和细节:

  1. 在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性
  2. 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的目静态成员


8.3代码块


  • 代码化块又称为初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过们包围起来。但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用

基本介绍:


基本语法:

[修饰符]{

 代码;

}



注释:

  1. 修饰符 可选,要写的话,也只能写 static
  2. 代码块分为两类,使用static 修饰的叫静态代码块,没有static修饰的,叫普通代码块
  3. 逻辑语句可以为任何逻辑语句
  4. :号可以写上,也可以省略

注意事项和细节:

  1. static代码块是类加载时执行,且只会执行一次
  2. 普通代码块是在创建对象的调用的,创建一次,调用一次
  3. 类什么时候被加载⭐️⭐️:
  • 创建对象实例时
  • 创建子类对象实例时,父类也会被加载
  • 使用类的静态成员时
  1. 创建一个对象时,在一个类的调用顺序
  • 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调
  • 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,若果有多个普通代码块和stat多个普通属性初始化,则按定义顺序调用)
  • 调用构造器
  1. 构造方法(构造器)的最前面其实隐含了 super()和调用普通代码块
  2. 创建子类时顺序⭐️⭐️:
  • 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
  • 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
  • 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
  • 父类构造方法
  • 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
  • 子类构造方法
  1. 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员


8.4单例设计模式


  • 就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法单例模式有两种方式:1. 饿汉式  2.懒汉式

基本介绍:


饿汉式:

  1. 构造器私有化
  2. 类的内部创建静态对象
  3. 向外暴露一个静态公共方法
  4. 特点:在类加载的时候就创建对象实例,可能存在资源浪费
class GirlFriend{
    private String name;
    private static GirlFriend gf = new GirlFriend("小花");
    private GirlFriend(String name) {
        this.name = name;
    }
    public static GirlFriend Instance() {
        return gf;
    }
}



懒汉式:

  1. 构造器私有化
  2. 定义一个static静态属性对象
  3. 提供一个public的static方法,返回一个对象
  4. 只有当使用(3)的方法时,才返回对象,再次调用时,返回上次创建的对象
  5. 特点:线程安全问题
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;
    }
}

对比:

  1. 最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建
  2. 饿汉式不存在线程安全问题,徽汉式存在线程安全问题
  3. 饿汉式存在浪费姿源的可能,对象实例都没有使用,那么饿汉式创建的对象就浪费,懒汉式是使用时才创建,就不存在这个问题


8.5  final



基本介绍:

  • final可以修饰类、属性、方法、局部变量
  • final 类,类不能被继承
  • final 方法,子类无法重写方法
  • final 属性,属性无法被修改
  • final 局部变量,局部变量无法被修改

注意事项和细节:

  1. final修饰的属性又叫常量,一般用XX_XX_XX 来命名
  2. final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置:定义时、构造器、代码块
  3. final修饰的属性是静态的,则初始化的位置只能是 定义时、静态代码块
  4. final类不能继承,但可以实例化对象
  5. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
  6. 如果一个类已经是final类了,就没有必要再将方法修饰成final方法
  7. final不能修饰构造器本身
  8. final 和static 往往搭配使用,效率更高,不会导致类加载-底层编译器做了优化处理
  9. 包装类(Integer, Double,Float,Boolean等都是final),String也是final类


8.6抽象类



基本介绍:

  • 用abstract 关键字来修饰一个类时,这个类就叫抽象类
  • 用abstract 关键字来修饰一个方法时,这个方法就是抽象方法,没有方法体
  • 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类

注意事项和细节:

  1. 抽象类不能被实例化
  2. 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
  3. 一旦类包含了abstract方法,则这个类必须声明为abstract
  4. abstract 只能修饰类和方法
  5. 抽象类可以有任意成员,抽象类本质还是类
  6. 抽象方法不能有主体
  7. 如果一个类继承了抽象类,则它必须实现抽象类的所有的抽象方法,除非它自己也声明为abstract类
  8. 抽象方法不能使用private、final 和static来修饰,因为这些关键字都是和重写相违背的

抽象类模版设计模式

  1. 编写方法cal(),可以计算某段代码的耗时时间
  2. 编写抽象方法job()
  3. 编写一个子类A,继承抽象类Template,井实现job方法
  4. 编写一个测试类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 接口 {
  自己属性;
  自己方法;
  //必须实现接口的抽象方法
}


  1. Jdk7.0前 接口里的所有方法都没有方法体(只能抽象方法)
  2. Jdk8.0后接口类可以有静态方法,defaut实现方法,也就是说接口中可以有方法的具体实现

注意事项和细节:

  1. 接口不能被实例化
  2. 接口中所有的方法是 public 和 abstrac 方法,接口中抽象方法,可以不用 abstract 修饰
  3. 普通类实现接口,就必须将该接口的所有方法实现
  4. 抽象类实现接口,可以不用实现接口的方法
  5. 一个类同时可以实现多个接口
  6. 接口中的属性,只能是 final 的,而且是 public static final 修饰符
  7. 接口中属性的访问形式:接口名.属性名
  8. 一个接口不能继承其它的类,但是可以继承多个别的接口
  9. 接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的

实现接口VS继承类:

  1. 继承的价值主要在于:解决代码的复用性和可维护性
  2. 接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法
  3. 接口比继承更加灵活,继承是满足 is -a的关系,而接口只需满足 like-a的关系
  4. 接口在一定程度上实现代码解耦

接口类型数组:

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. 定义在外部类局部位置上:1局部类内部(有类名) 2.匿名局部类(没有类名⭐️)
  2. 定义在外部类的成员位置上:1.成员内部类(无static修饰) 2.静态内部类(static修饰)


8.8.1局部内部类


  • 局部内部类是定义在外部类的局部位置,在方法中、代码块中,并且有类名

基本介绍:


注意事项和细节:

  1. 可以直接访问外部类的所有成员,包含私有的
  2. 不能添加访问修饰符,但是可以使用final修饰
  3. 作用域:仅仅在定义它的方法或代码块中
  4. 内部类访问外部类:直接访问
  5. 外部类访问内部类:创建对象再访问
  6. 外部其他类访问局部内部类:不能访问
  7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果访问外部类的成员使用"外部类名.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 类或接口(参数列表){

 类体;

};



底层:

  1. 本质: IA tiger = class Xxxx$1      class Xxxx$1 = new IA
  2. 接口的底层:class Xxxx$1 implement IA{@Overide}
  3. 本质: Father father = class Xxxx$2      class Xxxx$2 = new Father
  4. 类的底层:class Xxxx$2 extends Father{@Overide}

注意事项和细节:

  1. 匿名内部类既是一个类的定义同时本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征
  2. 可以直接访问外部类的所有成员,包含私有的
  3. 不能添加访问修饰符,因为它的地位就是一个局部变量
  4. 作用域:仅仅在定义它的方法或代码块中
  5. 匿名内部类方位外部类成员:直接访问
  6. 外部其他类访问匿名内部类:不能访问
  7. 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.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修饰

基本介绍:


注意事项和细节:

  1. 可以直接访问外部类的所有成员,包含私有的
  2. 可以添加任意访问修饰符
  3. 作用域:为整个外部类类体
  4. 成员内部类访问外部类:直接访问
  5. 外部类访问成员内部类;创建对象,再访问
  6. 外部其他类访问成员内部类:
  • 外部类.内部类  引用名 = 外部对象.new 内部类();
  • 外部类.内部类  引用名 = 外部对象.get();
  1. 如果外部类和成员内部类的成员重名时,成员内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.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修饰

基本介绍:


注意事项和细节:

  1. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
  2. 可以添加任意访问修饰符
  3. 作用域:整个外部类类体
  4. 静态内部类访问外部类:直接访问
  5. 外部类访问静态内部类:创建对象,再访问
  6. 外部其他类访问静态内部类:
  • 外部类.内部类  引用名 = new 外部类.内部类();
  • 外部类.内部类  引用名 = 外部对象.get();
  • 外部类.内部类  引用名 = 外部类.get();
  1. 如果外部类和静态内部类的成员重名时,静态内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.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类加载顺序⭐️⭐️



类什么时候被加载:

  • 创建对象实例时
  • 创建子类对象实例时,父类也会被加载
  • 使用类的静态成员时

创建一个对象时,在一个类的调用顺序

  1. 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调
  2. 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,若果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
  3. 调用构造方法

创建子类时顺序:

  1. 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
  2. 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
  3. 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
  4. 父类构造方法
  5. 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
  6. 子类构造方法


9 枚举和注解


  • 枚举是一组常量的集合,属于一种特殊的类,里面只包含一组有限的特定的对象

基本介绍:


9.1自定义类实现枚举



实现步骤:

  1. 构造器私有化
  2. 本类内部创建一组对象
  3. 对外暴露对象(通过为对象添加public final static修饰符)
  4. 提供get方法,但是不提供set方法

注意事项和细节:

  1. 不需要提供setxxx 方法,因为枚举对象值通常为只读
  2. 枚举对象/属性使用 final + static 共同修饰,实现底层优化
  3. 枚举对象名通常使用全部大写,常量的命名规范
  4. 枚举对象根据需要,也可以有多个属性


9.2enum关键字实现枚举



实现步骤:

  1. 关键字 enum 代替 class
  2. 行首:常量名(实参列表)
enum Season {
    SPRING("春天","温暖"),WINTER("冬天","寒冷");
    private String name;
    private String desc;
    private Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }



注意事项和细节:

  1. 使用enum 关键字开发一个枚举类时,默认会继承Enum类
  2. 传统的 public static final Season SPRING = new Season("春天”"温暖");简化成 SPRING("春天”,"温暖”),我们必须知道它调用的是哪个构造器
  3. 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
  4. 多个枚举对象,使用,间隔,最后;结尾
  5. 枚举对象必须放在枚举类行首

常用方法:

  • 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);
//        }
    }
}

实现接口:

  1. 使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum,而Java是单继承机制
  2. 枚举类和普通类一样,可以实现接口:enum 类名 implements 接口1,接口2{}
目录
相关文章
|
3月前
|
存储 前端开发 算法
毕业季--JavaSE高级面试题
毕业季--JavaSE高级面试题
|
3月前
|
Java 编译器
JavaSE学习之常见易错点总结--第一期
JavaSE学习之常见易错点总结--第一期
26 0
|
11月前
|
Java
JavaSE成神之路 - JDK安装
JavaSE成神之路 - JDK安装
|
存储 设计模式 安全
JavaSE笔记--韩顺平(入门详细版)(三)
JavaSE笔记--韩顺平(入门详细版)(三)
174 0
|
安全 Java 编译器
JavaSE笔记--韩顺平(入门详细版)(五)
JavaSE笔记--韩顺平(入门详细版)(五)
114 4
|
存储 网络协议 安全
JavaSE笔记--韩顺平(入门详细版)(四)
JavaSE笔记--韩顺平(入门详细版)(四)
145 2
|
存储 XML 安全
JavaSE笔记--韩顺平(入门详细版)(二)
JavaSE笔记--韩顺平(入门详细版)(二)
213 0
|
存储 Oracle IDE
javase基础教程(1)| 青训营笔记
javase基础教程(1)| 青训营笔记
54 0
javase基础教程(1)| 青训营笔记
|
Java 程序员 索引