Java SE - 基本语法

标识符命名规范

  1. 标识符可以包含英文字母26个(区分大小写)、0-9数字、$、_、中文字符(不推荐使用)
  2. 标识符不能以数字开头
  3. 标识符不能是关键字(关键字一共53个,其中常用关键字51个,保留关键字2个,main 和 java 不是关键字)
  4. 类名每个单词首字母大写,方法变量命名用驼峰命名法

数据类型

  1. Java的数据类型分为两大类:
    • 基本数据类型:byte、short、int、long、double、float、char、boolean
    • 引用数据类型:类、数组、接口
  2. 基本数据类型分类(一个字节占8个位):
    • byte:每声明一个byte类型变量,在内存中占1个字节
    • short:每声明一个short类型变量,在内存中占2个字节
    • int(默认):每声明一个int类型变量,在内存中占4个字节
    • long:每声明一个long类型变量,在内存中占8个字节,给long类型变量进行初始化时,需要在初始化值后面添加字母L或l
    • float:每声明一个float类型变量,在内存中占4个字节,给float类型变量进行初始化时,需要在初始化值后面添加字母F或f
    • double(默认):每声明一个double类型变量,在内存中占8个字节
    • char:
      • 内码规则:当char类型变量由底层源码进行声明初始化,每个字符在内存中占2个字节
      • 外码规则:当char类型变量由程序员自己进行声明初始化,每个字节根据开发环境的编码方式所确定的编程方式:
        • GBK编码:字母、数字、标点、中文每个字符占2个字节
        • UTF-8编码:字母、数字、英文标点每个字符占1个字节,中文每个字符占3个字节
    • boolean:
      • 在程序中声明初始化基本类型变量,每个boolean类型变量的初始化值占4个字节(栈)
      • 在程序中声明初始化布尔数组,数组中每个元素占用内存1个字节(堆)
  3. 给char类型变量进行初始化时,初始化值常用有两种表示方式,无论哪种表示方式都是以单字符形式进行使用

    • 单字符表示法 ‘单个字符’
    • 数字表示法(Unicode码表) 取值范围:0~65535

      • 字符0 十进制数字48
      • 字符A 十进制数字65
      • 字符a 十进制数字97

        常量的注意事项,常量优化机制

    • 给byte,short,char类型变量进行赋值操作时,如果右边的初始化值是字面值常量,且还在该数据类型的取值范围内,JVM的编译器自动在编译时期将其优化为byte,short,char类型数据,我们将这一优化称之为“常量优化机制”

    • 给变量进行赋值操作时,如果右边的初始化值是一个数学运算式子,且运算的符号两边都是字面值常量和运算后的结果还在该数据类型的取值范围内,JVM的编译器会将这一运算过程由运行时期提前至编译时期,在生成.class文件已运算完毕,我们将这一优化称之为“常量优化机制”

类型转换

  1. 隐式转换(自动转换)
    • 将取值范围较小的数据类型转换成取值范围较大的数据类型
    • 基本数据类型取值范围大小关系:
      • byte< short< int< long< float< double
      • char< int < long < float < double
  2. 显示转换(强制转换)
    • 将取值范围较大的数据类型转换为取值范围较小的数据类型
    • 格式
      • 取值范围较小的数据类型 变量名 = (取值范围较小的数据类型)取值范围较大的类型的数据值
  3. 基本数据类型转换的注意事项
    • 基本数据类型转换是七种数值类型之间的转换,与boolean类型无关
    • byte,short,char这三种数据类型只要参与数学运算,先将数据越级提升成int类型,在参与运算操作;如果没有参与数学运算,依然遵循取值范围的特点转换
  4. 三元运算符类型转换
    • 如果其中有一个是常量,按照自动类型转换处理成一致的类型
    • 如果都是常量
      • 一个是char,另一个是【0-65535】之间的整数,按char处理
      • 一个是char,一个是其他,按照自动类型转换规则处理成一致的类型
        1. public static void main(String[] args) {
        2. char x = 'x';
        3. int i = 10;
        4. System.out.println(true? x : i); //120
        5. System.out.println(true? 'x' : 10);// 'x'
        6. System.out.println(true? 'x' : 0.2112);//120.0
        7. }

运算符

  • 常见运算符的分类
    • 算术运算符 + - * / % ++ —
    • 赋值运算符 = += -= *= /= %=
    • 关系运算符 < <= > >= == !=
    • 逻辑运算符
      • && 短路与
      • || 短路或
      • ! 非
    • 三元运算符
      • 关系表达式 ? 结果值1 : 结果值2;
      • 确认关系表达式的结果的同时,同意结果值1和结果值2的数据类型
      • true执行结果值1 false执行结果值2
    • 位运算符
      • 原码反码补码
        • 相同点:
          在程序中都是针对二进制数据进行定点表示法:
          (1)原反补针对二进制规定了符号位,最高位即符号位,正数为0,负数为1
          (2)原反补针对存储占用的字节数进行二进制补全,不足位数,在符号位后面进行补0操作
        • 不同点:
          原码:是屏幕显式数据的二进制定点表示法
          补码:计算机中所有的数据操作都拿原码的补码形式进行计算操作
          反码:针对原码和补码进行转换的中间量 ```java 数据存储操作过程: 1.显式的数据==>数据的原码 如果数据是正整数,符号位确认为0,并补足位数 如果数据是负整数,符号位确认为1,并补足位数 2.数据的原码==>数据的反码 如果数据是正整数,数据的反码与其原码相同 如果数据是负整数,数据的反码是针对原码进行逐位取反,符号位保持不变 3.数据的反码==>数据的补码 如果数据是正整数,数据的补码与其反码相同 如果数据是负整数,数据的补码在其反码的基础上进行+1运算 4.数据的补码==>结果的补码 数据的存储或操作 5.结果的补码==>结果的反码 如果结果是正整数,结果的反码与其补码相同 如果结果是负整数,结果的反码在其补码的基础上进行-1运算 6.结果的反码==>结果的原码 如果结果是正整数,结果的原码与其反码相同 如果结果是负整数,结果的原码是针对反码进行逐位取反,符号位保持不变 7.结果的原码==>显式的结果 如果结果是正整数,直接转换显式结果 如果结果是负整数,忽略符号位,将其直接转换显式结果并添加负号

将int类型的130强制转换成byte类型 1.显式的数据==>数据的原码 数据的原码:00000000 00000000 00000000 10000010 2.数据的原码==>数据的反码 数据的反码:00000000 00000000 00000000 10000010 3.数据的反码==>数据的补码 数据的补码:00000000 00000000 00000000 10000010 4.数据的补码==>结果的补码 结果的补码:10000010 5.结果的补码==>结果的反码 结果的反码:10000001 6.结果的反码==>结果的原码 结果的原码:11111110 7.结果的原码==>显式的结果 显式的结果:-126

  1. - 按位运算符&:
  2. - 当两位相同为1时才返回1
  3. - 按位运算符|:
  4. - 只要有一位为1 即可返回1
  5. - 按位运算符^:
  6. - 当两位相同时返回0,不同时返回1
  7. - 按位运算符 ~
  8. - 将操作数的每个位(包括符号位)全部取反
  9. - 移位运算符<<
  10. - 格式 指定数据 << 移动位数
  11. - 将指定数据的二进制往左移动指定的位数,符号位也随之移动,如果低位出现了空位,需要补0进行占位
  12. - 移位运算符>>
  13. - 格式 指定数据 >> 移动位数
  14. - 将指定数据的二进制往右移动指定的位数,符号位也随之移动,如果高位出现了空位,需要补和符号位相同的数字进行占位
  15. - 移位运算符>>>
  16. - 格式 指定数据 >>> 移动位数
  17. - 将指定数据的二进制往右移动指定的位数,符号位也随之移动,如果高位出现了空位,需要补0进行占位
  18. <a name="fb5550cd"></a>
  19. ### 运算符的优先级
  20. 1. Java中运算符的优先级和数学中的优先级不一样,不是谁优先级高,就最先执行谁,而是式子从左到右依次执行,遇到运算符的时候,再考虑优先级问题,先执行谁后执行谁的问题
  21. 2. 如果表达式中出现了自增自减运算符,且=的两边出现了相同的变量,这个时候需要特殊注意优先级问题
  22. ```java
  23. int x = 3;//3 4 3
  24. x = x++; //先使用后运算:先使用3存储到内存(操作栈)后运算,3自增为4
  25. System.out.println("x = " + x);//3
  26. /*
  27. Java先运算x++这个表达式,并将结果存储在一个空间temp中,于是此时temp = 3
  28. 然后自增生效,对x自加,这是x = 4
  29. 接着Java执行x=这一赋值部分,因为x++运算的结果是存储在temp中,所以实际上是x = temp,所以x又变为了3
  30. */
  1. 如果表达式中出现了扩展的赋值运算符,需要将扩展的赋值运算符拆分成基础赋值运算符再进行计算
    int a = 3;
    a *= a + 5;//a = (a + 5) * a
    System.out.println("a = " + a);//24
    

for、while、do while三种循环的区别

  1. 循环语句的执行次数(当循环条件语句为false时,循环体语句至少执行的次数)
    for语句和while语句当循环条件语句为false时,循环体语句至少执行0次
    do while语句当循环条件语句为false时,循环体语句至少执行1次
  2. 实际开发中如何选择
    • 有明显的循环次数或循环范围,选择for语句
    • 没有明显的循环次数或循环范围,选择while语句
    • 在实际开发中不会选择do while语句

if语句,for语句,while语句如果{}中的语句体有且仅有一行时,{}可以省略不写

switch语句的注意事项

  1. switch语句()中选择值的数据类型只支持以下几种:
    • 基本类型:原则上在内存中只支持int类型,因为自动转换可以衍生出byte,short,char
    • 引用类型:
      • JDK5.0(包含)以后:支持枚举Enum类型
      • JDK7.0(包含)以后:支持字符串String类型
  2. switch 语句中的 default 和 if 语句的第三种格式中 else 类似,可以省略不写
  3. switch语句中的default和case位置可以互换,但不影响执行流程
  4. switch语句中的break可以省略不写,但是会出现case穿透效果

    //break省略不写,以下代码执行结果为:你好    我好    大家好
    switch (3){
    default:
      System.out.println("你好");
    case 1 :
      System.out.println("我好");
    case 2 :
      System.out.println("大家好");
    }
    

    流程控制语句

  5. break

    • 场景:switch语句中,循环语句中
    • 作用:结束循环的语句
  6. continue
    • 场景:循环语句中
    • 作用:跳出本次循环,继续下一次循环
  7. return
    • 场景:方法中
    • 作用:结束本次方法

在和控制语句相同的作用域内,控制语句的后面不可以编写任何代码,否则报错,因为后面的代码永远执行不到

for死循环和while死循环的区别

  1. 在实际开发中,更推荐使用while的死循环格式,原因:格式简洁明了,死循环是属于没有明显的循环范围或次数
  2. 在使用两种死循环执行相同的代码时,for的死循环格式执行效率比while的死循环格式更高,往往将for的死循环格式应该在算法中或者是源码中,而在其它的情况下,可以忽略提高的效率,使用while的死循环
  3. for的死循环格式:
    for ( ; ; ) {
    循环体语句
    }

嵌套循环制定循环名称

  • 标识符 : 循环语句
public class Test03 {
    public static void main (String[] args){
         a : for (int i = 2; i < 101 ; i++ ) {
            for (int j = 2; j < i ; j++ ) {
                if (i % j == 0){
                    continue a;
                }
            }
            System.out.println(i +"是质数");
            }
        }
    }

JVM虚拟机

  1. 方法进栈和出栈遵循“先进后出,后进先出”原则
  2. 方法的出栈是指在栈内存中立刻消失

方法的重载

  1. 方法重载的前提条件
    1. 必须在同一个类中(或字父类继承关系中)
    2. 方法名必须相同
    3. 形参列表必须相同(至少满足以下一点)
      • 形参的数据类型不同
      • 形参的个数不同
      • 形参的数据类型顺序不同
  2. 方法重载的注意事项

    • 执行重载的方法具体执行哪个和方法本身无关,和方法的实参列表和形参列表有关 ```java public class MethodDemo12 { public static void main (String[] args) { method(3,4);//执行(int , int) //方法重载中实参数据会自动匹配适合他的形参列表

    }

    public static void method (int a , double b) {

    System.out.println("int double");
    

    }

    public static void method (double a , int b) {

    System.out.println("double int");
    

    }

    /* public static void method (byte num) {

    System.out.println("byte");
    

    } */

    public static void method (short num) {

    System.out.println("short");
    

    }

    public static void method (int num) {

    System.out.println("int");
    

    }

    public static void method (long num) {

    System.out.println("long");
    

    } } ```

    方法进行传参时的特点

  3. 如果是基本参数类型

    1. 进行参数传递的是基本类型的数据值
    2. 形式参数数据值的改变不会影响实际参数数据值
  4. 如果参数是引用数据类型
    1. 进行参数传递的时引用数据类型
    2. 形式参数地址值的改变不会影响实际参数地址值
    3. 形式参数地址值不改变,形式参数地址值中的元素值的改变影响实际参数地址值中的元素值

可变参数

  1. 形参列表为 数据类型… 数组名 (int… arr)的为可变参数
  2. 可变参数的注意事项
    1. 当方法声明时,形参列表中除了可变参数外还存在其他参数,需要将可变参数写在形参列表的最后一个位置
    2. 当声明方法时,形参列表中的可变参数最多只能有一个

静态变量,实例变量,局部变量三种变量的区别

  1. 代码中的位置不同
    1. 静态变量:类中方法外
    2. 实例变量:类中方法外
    3. 局部变量:方法内部或方法声明上
  2. 内存中的位置不同
    1. 静态变量:堆内存
    2. 实例变量:堆内存
    3. 局部变量:栈内存
  3. 默认值不同
    1. 静态变量:含有默认值
    2. 实例变量:含有默认值
    3. 局部变量:没有默认值
  4. 代码中作用域不同
    1. 静态变量:所属类中
    2. 实例变量:所属类中(静态成员除外)
    3. 局部变量:所属方法中
  5. 内存中生命周期不同
    1. 静态变量:随着类的加载而加载,随着类的回收而消失
    2. 实例变量:随着对象的创建而加载,随着对象的回收而消失
    3. 局部变量:随着方法的调用而加载,随着方法的出栈而消失
  6. 变量的加载方式和次数不同
    1. 静态变量:随着类的加载而加载,只加载唯一的一次
    2. 实例变量:随着对象的创建而加载,每创建一次对象就会加载一次
    3. 局部变量:随着方法的调用而加载,每调用一次方法就会加载一次
  7. 修饰符不同

    1. 静态变量:可以添加修饰符,但必须含有static关键字修饰
    2. 实例变量:可以添加修饰符
    3. 局部变量:只能被final关键字修饰

      静态方法的注意事项

  8. 静态方法随着类的加载而加载,而且只加载唯一的一次

    1. 静态方法加载:静态方法进去静态区
    2. 静态方法调用:静态方法进入栈内存
  9. 静态方法可以通过类名和对象名进行调用,推荐使用类名进行调用
  10. 在静态方法中不可以使用非静态成员
  11. 在静态方法中不可以使用this关键字

构造器

  1. 格式 : 修饰符 类名 (){}
  2. 作用:进行成员的初始化,如果是有参的构造器可以针对对象成员进行赋值操作
  3. 注意:
    1. 构造器的声明格式和使用特点和方法类似,但是有别于方法,构造器也称之为”构造方法”
    2. 构造器的名字必须与类名相同,否则编译报错
    3. 构造器没有返回类型,而且连void关键字都不能有
    4. 当一个类没有任何的构造器的时候,JVM的编译器自动提供一个public无参的构造器,供其创建对象时进行对象成员的初始化;一旦这个类含有任何的构造器时,JVM的编译器不再进行提供
    5. 构造器支持方法的重载
    6. 接口中没有构造器
    7. 构造器中的{}有很多隐式的代码,执行的时候遵循优先级
      1. 第一优先级:隐式或显式的super(实参),显式的this(实参)
      2. 第二优先级:实例成员和构造器代码块
      3. 第三优先级:构造器中除了第一优先级的显式代码

JavaBean标准类

  1. 一个.java文件中只允许声明一个类
  2. 必须有:
    1. 所有的成员变量和成员常量必须进行私有化
    2. 针对每个被私有化的成员变量成员常量提供一对公共的访问方式
    3. 无参构造器
  3. 可以有:
    1. 根据实际需求添加有参的构造器
    2. 根据实际需求添加构造器代码块
    3. 根据实际需求添加静态代码块
    4. 根据实际需求添加成员内部类
    5. 根据实际需求添加toString(),equals(),hashCode()

初始化

  1. 有继承关系的实例初始化过程
    1. 显示或隐式的super(实参),显式的this(实参)
    2. 实例成员和构造器代码块,谁在前,优先执行谁
    3. 构造器中除了super(实参)或this(实参)的显式代码
  2. 有继承关系的类初始化过程
    1. 先初始化父类
    2. 静态成员和静态代码块谁在前优先加载谁

静态成员随着类的加载而加载,而且只加载唯一的一次

初始化分析

public class SuperClass {
    int superNum = getSuperNum();
    public int getSuperNum () {
        System.out.println("父类的实例变量显式赋值操作");
        return 10;
    }
    public SuperClass() {
        System.out.println("父类的无参构造器中显式代码");
    }
    {
        System.out.println("父类的构造器代码块");
    }
}
public class SubClass extends SuperClass{
    int subNum = getSubNum();
    public int getSubNum () {
        System.out.println("子类的实例变量显式赋值操作");
        return 10;
    }
    public SubClass() {
        this(1);
        System.out.println("子类的无参构造器中显式代码");
    }
    public SubClass(int num) {
        System.out.println("子类的有参构造器中显式代码");
    }
    {
        System.out.println("子类的构造器代码块");
    }
}
public class InitDemo03 {
    public static void main(String[] args) {
        new SubClass();
    }
}
//父类的实例变量显式赋值操作
//父类的构造器代码块
//父类的无参构造器中显式代码
//子类的实例变量显式赋值操作
//子类的构造器代码块
//子类的有参构造器中显式代码
//子类的无参构造器中显式代码
public class SuperClass {
    int num = 10;
    public SuperClass() {
        //多态:实际上是调用的是子类的method方法
        this.method();
    }
    public void method () {
        System.out.println("num = " + this.num);
    }
}
public class SubClass extends SuperClass{
    int num = 20;
    public SubClass() {
        super();
    }
    public void method () {
        //当调用子类的method方法时,num还没被实例化
        System.out.println("num = " + this.num);
    }
}
public class InitDemo05 {
    public static void main(String[] args) {
        new SubClass();
    }
}
//num = 0
public class Test02 {
 public static void main(String[] args) {
 Father f = new Son();
 System.out.println(f.x);
 }
}
class Father{
 int x = 10;
 public Father(){
 this.print();
 x = 20;
 }
 public void print(){
 System.out.println("Father.x = " + x);
 }
}
class Son extends Father{
 int x = 30;
 public Son(){
 this.print();
 x = 40;
 }
 public void print(){
 System.out.println("Son.x = " + x);
 }
}

一维数组

数组的基础语法

  1. Java语言中的数组是一种引用数据类型,不属于基本数据类型,所以数组对象是在堆内存中,数组的父亲是Object
  2. 数组可以同时容纳多个元素,可以存储基本数据类型的数据,也可以存储引用类型的数据
  3. Java中数组类型要求统一,int类型数组只能存储int类型数据
  4. 数组中如果存储的是“Java对象”的话,实际上存储的是对象的“引用”(对象的内存地址)
  5. 数组一旦创建,数组长度不可变
  6. 所有数组都有length 属性,用来获取数组中元素的个数
  7. 数组在内存方面存储时,数组中的元素内存地址都是连续的。数组是一种数据结构
  8. 数组的内存地址就是第一个元素的内存地址
  9. 数组每一个元素都是有下标的,下标从0开始,以1递增,最后一个下标是Lengeh - 1

    数组这种数据结构的优缺点

  10. 优点:查询/查找/检索某个下标上的元素时效率很高,查询效率最高的一个数据结构

    1. 为什么检索效率高?
      1. 每一个元素的内存地址在空间存储上是连续的
      2. 每一个元素类型相同,所以占用空间大小是一样的
      3. 知道第一个元素的内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式,就可以计算出某个下标上元素的内存地址,直接通过内存地址定位元素,所以数组的检索效率是最高的
  11. 缺点:

    1. 由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作
    2. 数组不能存储大量数据,因为很难在内存上找到一块特别大的连续的空间

      定义初始化数组

  12. 定义数组:

    1. int[] array1;
    2. double[] array2;
    3. String[] array3;
    4. Object[] array4;
  13. 初始化数组
    1. 静态初始化数组:
      1. int[] array = {100, 2100, 2}; // int array[] = {1, 3, 4, 2} 这是C++风格,不建议Java中使用
      2. int[] array = new int[]{100,2100,2}//这种方式 是静态初始化数组的标准形式
    2. 动态初始化数组;
      1. int[] array = new int[5]; //初始化5个长度的int类型数组,每个元素的默认值为0
      2. String[] names = new String[6]; // 初始化6个长度的String类型数组,每个元素默认值为null
  14. 遍历一维数组

    for(int i = 0, i < a.length; i++){
     System.out.println(a[i]);
    }
    for(int i = a.length - 1; i >=0 ,i--){
     System.out.println(a[i]);
    }
    
  15. 数组下标越界异常 ArrayIndexOutofBoundsException

  16. 方法传一个静态数组语法必须这样写

    public static void printArray(int[] array){}
    //没有这种语法
    //printArray({1, 2, 3});
    printArray(new int[]{1, 2, 3});
    printArray(new int[5]);
    

    数组声明初始化的注意事项

  17. 数组中的元素支持变量的类型转换,保证内存中是用一种数据类型即可

  18. 数组不可以通过动静结合的方式进行初始化
  19. 静态初始化的简化版不可以先声明后初始化

    1. 原因:静态初始化简化版直接声明初始化时,是根据数组名前面的数据类型【】在内存中申请开辟内存空间,new 数据类型[]由JVM隐式进行提供
    2. 静态初始化简化版如果先声明后初始化,在初始化过程中无法直接获取数组名前面的数据类型【】,JVM不会隐式提供new 数据类型【】

      main方法的String数组

  20. String[] args 这个数组是留给用户的,用户可以在控制台上输入参数,这个参数会自动转化为“String[] args”

  21. 这样运行程序:java ArrayTest abc def xyz 将会自动转化为{“abc”, “def”, “xyz”}

    public static void main(String[] args){
     if(args.length !=2){
         System.out.println("使用该系统时请输入程序应用参数,包括用户名和信息");
     }
     String username = arg[0];
     String password = arg[1];
     //if(username.equals("admin")&&password.equals("123")){  //这样写容易出现空指针异常
     if("admin".equals(username) && "123".equals(password)){
         System.out.println("欢迎回来");
     }else{
         System.ouy.println("验证失败");
     }
    }
    //javac 运行命令
    javac *.java admin l23
    

    数组扩容

  22. 数组一旦创建不能更改长度,所以数组扩容是创建一个新的数组然后再把旧的数组拷贝到新的数组

  23. Java源码

    public static native void arraycopy(Object src,  int  srcPos,Object dest,
                                     int destPos,int length);
    

    System类中的arraycopy方法五个参数列表:源数组,源数组拷贝起始位置,目标数组,拷贝目标数组起 始位置,拷贝长度

  24. 实操代码

    public static void main(String[] args){
     int[] src = {1, 2, 3, 4};
     int[] dest = new int[10];
     System.arraycopy(src, 1, dest, 2, 3):
    }
    

    二维数组

  • 二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素都是一个一维数组

    二维数组的声明

  1. 数据类型[][] 数组名;(推荐)
  2. 数据类型 数组名[][];
  3. 数据类型[] 数组名[];

单例设计模式

  1. 含义:解决创建唯一实例对象的专用解决方案
  2. 分类:

    1. 立即加载(饿汉式) 在类加载的同时进行对象的创建

      private static CEO ceo = new CEO();
      private CEO () {}
      public static CEO getCEO() {
      return ceo;
      }
      
    2. 延迟加载(懒汉式,仅适用于单线程环境中) 什么时候使用对象什么时候再进行对象的创建

      private static CEO ceo;
      private CEO () {}
      public static CEO getCEO () {
      if (ceo == null) {
      ceo = new CEO();
      }
      return ceo;
      }
      
  • 通过“类名.”的方法访问

    没有static的方法怎么调用?

  • 方法没有static ,这样的方法被称为实力方法(对象方法,对象级别的方法)

  • 先new一个对象,通过“引用.”去访问

    空指针异常导致的最本质的原因是什么?

  • 空引用访问“实例相关的数据”,会出现空指针异常

  • 实例相关的包括:实例变量 + 实例方法 (属性 + 方法)

    变量的分类:

  • 变量根据声明的位置进行划分

    • 在方法体当中声明的变量叫做:局部变量
    • 在方法体外声明的变量叫做:成员变量(成员变量分为实例变量和静态变量)

      实例变量和成员变量有什么区别

  • 实例的都是和对象相关的,访问时采用“引用.”的方式进行访问。需要先new对象。

    • 实例相关的,必须先有对象,才能访问,可能会出现空指针异常。
  • 静态的都是和类相关的,访问时采用“类名.”的方式进行访问。不需要new对象。

    • 静态相关的,不需要对象的参与就能访问,没有空指针异常的发生。

      方法什么时候定义为实例方法?

  • 实例方法一般都是描述了一个行为,如果说该行为必须由对象去触发。那么该方法定义为实例方法。

  • 当这个方法体中访问了实例变量,那么这个方法一定是实例方法

    使用static关键字可以定义:静态代码块

  • static{

    • java语句
    • Java语句
  • }
  • 静态代码块在类加载的时候执行,只执行一次,并且在main方法之前执行
  • 静态代码块里声明的变量都是局部变量,只在块内有效
  • 静态代码块一般是按照自上而下的顺序进行执行
  • 静态代码块中不可以使用非静态成员,不可以使用this关键字

    遇到的Java程序,有顺序要求的是哪些?

  1. 对于一个方法来说,方法体中的代码是有顺序的,遵循自上而下的顺序进行执行
  2. 静态代码块1和静态代码块2是有先后顺序的
  3. 静态代码块和静态变量是有先后顺序的

    实例语句块是什么?

  • {
    • java语句
    • Java语句
    • Java语句
  • }
  • 实例语句块在构造方法执行前执行,每执行一次构造方法都执行一次实例语句块

    代码执行顺序

  1. 静态代码块(全部执行完)
  2. main方法
  3. 实例语句块
  4. 构造方法

    Java三大变量存储位置

  • 静态变量在类加载的时候初始化,存放在方法区
  • 方法执行被压栈,局部变量在栈中
  • 堆中new对象,实例变量在堆中存放

    类加载执行顺序?

    class Student{
      static{
          System.out.println("a");
      }
      public void doSome(){
          System.out.println("b");
      }
      {
          System.out.println("e");
      }
    }
    public class Review{
      static{
          System.out.println("c");
      }
      public static void main(String[] args){
          System.out.println("d");
          Student d = new Student();
          System.out.println("f");
          d.doSome();    
      }
    }
    
  • 静态代码块 > 实例语句块 > main方法

  • 代码执行结果为:c d a e f b

    this关键字:

  1. this是一个关键字 ,全部小写
  2. 一个对象一个this
  3. this是一个变量,是一个引用,this保存当前对象的内存地址,指向自身,严格意义上来说this就是“当前对象”
  4. this 存储在堆内存对象的内部
  5. this可以使用在实例方法当中,谁调用这个方法,谁就是this,this代表当前对象,静态方法中不存在当前对象,用于区分同一个类中同名的实例变量和局部变量
  6. this也可以在构造方法中也可以使用,通过当前的构造方法去调用另一个本类的构造方法,可以使用以下的语法格式:this()只能出现在构造器第一行

    public Date(){
     /*
    
     this.year = 1970;
     this.month = 1;
     this.day = 1;
     */
     this(1970,1,1)
    }
    public Date(int year, int month, int day){
     this.year = year;
     this.month = month;
     this.day = day;
    }
    

    this关键字的第三种用法

  7. 场景:父类的实例方法中或父类的构造器中

  8. 格式:
    1. this.实例变量名;
    2. this.实例方法名(实参)
  9. 作用:使用多态形式的对象调用实例变量或实例方法
  10. 含义:哪个对象调用了this关键字所在的构造器或实例方法,this关键字就代表所在类型(this关键字所在的类名)的那个多态形式的对象

    public class SuperClass{
     int num = 10;
     public SuperClass(){
         //这个相当于 this.method()
         //其实调用的时子类的method方法
         method();
     }
     public void method(){
         //这个相当于 this.num
         System.out.println("num :" + num);
     }
    }
    public class SubClass{
     int num = 10;
     public SubClass(){
         method();
     }
     //构造器隐式代码还没实行完毕,所以num是默认值0
     public void method(){
         System.out.println("num :" + num);
     }
    }
    public class Test{
     public static void main (String[] args){
         SubClass sc = new SubClass();
     }
    }
    //输出结果为
    //num : 10
    

    this关键字的第四种用法

  11. 场景:成员内部类的实例方法或成员内部类的构造器中

  12. 格式:
    1. 外部类类名.this.实例变量名
    2. 外部类类名.this.实例方法名(实参)
  13. 作用:
    1. 用来区分外部类实例变量和内部类实例变量或局部变量同名的情况
    2. 用来区分外部类实例方法和内部类实例方法同名的情况
  14. 含义:哪个对象调用了“外部类类名.this.”的实例方法或构造器,“外部类类名.this”就代表哪个内部类对象的外部类对象
    public class SuperClass {
     int num = 10;
    }
    public class SubClass extends SuperClass {
     int num = 20;
     class Inner {
         int num = 30;
         public void method () {
             int num = 40;
             System.out.println(num);//40
             System.out.println(this.num);//30
             System.out.println(SubClass.this.num);//20
             System.out.println(SubClass.super.num);//10
         }
     }
    }
    public class Test {
     public static void main(String[] args) {
         //创建内部类对象
         SubClass.Inner inner = new SubClass().new Inner();
         inner.method();
     }
    }
    


继承

  1. 子类继承父类会继承父类除构造方法外所有的属性和方法(私有的属性也可以继承,只不过子类无法直接进行访问,需要公共访问方式进型获取或存储
  2. 继承的作用
    1. 基本作用:子类继承父类,代码可以得到复用
    2. 重要作用:因为有了继承关系,才有了方法覆盖和多态机制
  3. Java继承只支持单继承,不支持多继承,但是可以间接继承
  4. 任何一个类,没有显示继承任何类,默认继承Object类,Object是所有类的父类

    什么情况下可以使用继承?

  • 凡是采用”is a”能描述的,都可以继承,例如:
    • Cat is a Animal : 猫是一个动物
    • Dog is a Animal : 狗是一个动物
    • class Cat extends Animal{} (Cat is a Animal)
    • Cat 和 Dog 都能继承 Animal

当源码当中一个方法以“ ;”结尾,并且修饰符列表中有“native”关键字,表示底层调用C++写的dll程序(dll动态链接库文件)


toString方法

  • toString方法就是将对象转化成字符串
  • System.out.println(引用); 当直接输出一个人“ 引用”的时候,println()方法会自动调用“引用.toString()”,然后输出toString()方法的执行结果。

方法覆盖

什么条件满足之后能构成_方法重载_overload?

  • 当在一个类当中,如果功能相似的话,建议将名字定义的一样,这样代码美观,并且方便编程。
  1. 在同一个类当中
  2. 方法名相同
  3. 参数列表不同(参数个数不同、参数类型顺序不同、类型不同)

    在代码级别上怎么构成方法覆盖呢?

  4. 两个类必须要有继承关系

  5. 重写之后的方法和之前的方法具有:
    1. 相同的返回值类型
      1. 对于返回值类型是基本数据类型来说,必须一致
      2. 对于引用数据类型来说,重写之后的返回值类型可以是父类返回值类型的子类(但意义不大)
    2. 相同的方法名
    3. 相同的形式参数列表
  6. 访问权限不能更低,可以更高
  7. 重写之后的方法不能比之前的方法抛出更多的异常,可以更少·

    注意事项

  8. 方法覆盖只是针对于方法,和属性无关

  9. 私有方法无法覆盖
  10. 构造方法无法继承,所以构造方法也 不能被覆盖
  11. 方法覆盖只是针对于实例方法,静态方法覆盖

    方法重载和方法覆盖有什么区别

  12. 方法重载发生在同一个类当中

  13. 方法覆盖是发生在具有继承关系的父子之间
  14. 方法重载是一个类中,方法名相同,参数列表不同
  15. 方法覆盖是具有继承关系的父子类,并且重写之后的方法必须和之前的方法一致:

方法名一致、参数列表一致、返回值类型一致


多态

多态的基础语法

  1. 向上转型:子—>父 (自动类型转换)
  2. 向下转型:父—>子 (强制类型转换,需要加强制类型转换符)

无论是向上转型还是向下转型,两种类型之间必须有继承关系

多态关系中属性和多态无关

  • 实例变量的执行和多态没有任何关系
    public class SuperClass{
      int num = 10;
    }
    public class SubClass extends SuperClass{
      int num = 20;
    }
    public class Test{
      public static void main (String[] args) {
          SuperClass sc = new SubClass();
          //变量的执行不是取决于初始化值的数据类型,而是取决于变量的数据类型
          //输出结果为10
          System.out.println(sc.num);
      }
    }
    

    什么是多态

  1. 多态是父类型引用指向子类对象
  2. 多态必须有继承关系或实现关系
  3. 多态分为编译阶段和运行阶段

    1. 编译阶段:对于编译器来说,编译器只知道a2的类型是Animal,所以编译器在检查语法的时候会去Animal.class字节码文件中找move方法,找到了绑定上move()方法,编译器通过,静态绑定成功。(编译阶段属于静态绑定)
    2. 运行阶段:运行阶段的时候,实际上在堆内存中创建的Java对象Cat对象,所以move的时候,真正参与move的对象是一只猫,所以运行阶段会动态执行Cat对象的move()方法。这个过程属于运行阶段绑定。(运行阶段绑定属于动态绑定)
      public class Animal{
      public void move(){
        System.out.println("动物在移动");
      }
      public static void main(String[] args){
        //向上类型转换
        Animal a2 = new Cat();
        Animal a3 = new Birds();
        a2.move();
        a3.move();
        Cat c1 = (Cat) a3;
        c1.catchMouse();
        //判断引用指向的对象的类型
        if(a2 instanceof Cat){
            //向下转型
            Cat c2 = (Cat) a2;
            c2.move();
        }
      }
      }
      class Cat extends Animal{
      public void move(){
        System.out.println("Cat在走猫步");
      }
      public void catchMouse(){
        System.out.println("猫正在捉老鼠");
      }
      }
      class Birds extends Animal{
      public void move(){
        System.out.println("Birds在飞翔");
      }
      public void sing(){
        System.out.println("Birds在唱歌");
      }
      }
      
  4. 多态表示多种形态:

    1. 编译的时候是一种形态
    2. 运行的时候另一种形态

      什么时候必须使用“向下转型”

  • 不要随便做强制类型转换
  • 当你需要访问的是子类对象中“特有”的方法,此时必须进行向下类型转换

    java.Lang.ClassCastException异常

  1. 运算符:instanceof(运行阶段动态判断)
  2. instanceof可以在运行阶段动态判断引用指向的对象的类型
  3. instanceof的语法:(引用 instanceof 类型)
  4. instanceof运算符的结果只能是:true/false
  5. c是一个引用,c变量保存了内存地址指向了堆中的对象
    1. 假设(c instanceof Cat)为true表示:c引用指向的堆内存地址的java对象是一个Cat
    2. 假设(c instanceof Cat)为false表示:c引用指向的堆内存地址的java对象不是一个Cat

      什么是开闭原则?

  • 软件开发原则有七大原则(不属于Java,这个开发原则属于整个开发行业),其中有一条最基本的原则:ocp(开闭原则)
  • 对扩展开放(你可以额外添加),对修改关闭(很少修改现有程序)
  • 在软件的扩展过程当中,修改的越少越好

    多态在开发中的作用

  • 降低程序的耦合度,提高程序的扩展力

    public class Master{
      public void feed(Dog d){}
      public void feed(Cat a){}
    }
    
  • 以上的代码表示 :Master和Dog以及Cat的关系很紧密(耦合度高)

    public class Master{
      public void feed(Pet pet){}
    }
    
  • 以上的代码表示:Master和Cat以及Cat的关系就很脱离了,Master关注的是Pet类,这样Master和Dog以及Cat的耦合度就降低了,提高了软件的扩展性


super

基本语法

  1. super能出现在实例方法和构造方法中
  2. super的语法是:“super.”(子类调用父类的特征)和“super()”(子类调用父类的构造方法)
  3. super不能使用在静态方法中
  4. super.大部分情况下是可以省略的
  5. super.在子类和父类有相同的属性时 ,子类想访问父类的特征,super.不能省略
  6. super()只能出现在构造方法第一行,通过当前的构造方法调用父类中的构造方法,目的是:创建子类对象,先初始化父类特征

    重要结论

  • 当一个构造方法第一行既没有this()又没有super()的话,默认会有一个super()

表示通过当前子类的构造方法调用父类的无参数构造方法,所以必须保证父类的无参 数构造方法是存在的

  • this()和super() 不能共存,他们都是只能出现在构造方法第一行
  • 无论怎样,父类的构造方法是一定会执行的
  • super 不是引用,也不保存内存地址,super也不指向任何对象,只代表当前对象内部的那一块父类型的特征

    super的第三种用法

  1. 场景:实现类的实例方法中
  2. 格式:父接口名.super.默认方法名(实参)
  3. 作用:调用指定父接口的默认方法
  4. 含义:哪个对象调用的super的默认方法,super就代表哪个对象的指定父接口引用

super关键字的第四种用法

  1. 场景:成员内部类的实例方法或成员内部类的构造器中
  2. 格式:
    a. 外部类类名.super.实例变量名
    b. 外部类类名.super.实例方法名(实参)
  3. 作用:
    a. 用来区分外部类实例变量和内部类实例变量或局部变量同名的情况
    b. 用来区分外部类实例方法和内部类实例方法同名的情况
  4. 含义:哪个对象调用了“外部类类名.super”的实例方法或构造器,“外部类类名.super”就代表哪个内部类对象的外部类对象的父类引用

    public class SuperClass {
     int num = 10;
    }
    public class SubClass extends SuperClass {
     int num = 20;
     class Inner {
         int num = 30;
         public void method () {
             int num = 40;
             System.out.println(num);//40
             System.out.println(this.num);//30
             System.out.println(SubClass.this.num);//20
             System.out.println(SubClass.super.num);//10
         }
     }
    }
    public class Test {
     public static void main(String[] args) {
         //创建内部类对象
         SubClass.Inner inner = new SubClass().new Inner();
         inner.method();
     }
    }
    

    final

  5. final修饰的方法无法被重写

  6. final修饰的类无法被继承(abstract 和 final 关键字不能同时修饰类)
  7. final修饰的变量,只能被赋一次值
  8. final修饰的引用,只能指向一个对象,并且他只能永远指向该对象,无法指向其他对象,但是对象内的数据可以修改
  9. final修饰的实例变量,必须手动赋值,系统不赋默认值,在变量后面赋值可以,在构造方法中赋值也可以
  10. public static final 常量 ,先声明
  11. 直接声明初始化的自定义常量会被JVM识别为“字面值常量”
    1. 直接声明初始化:修饰符 final 数据类型 常量名 = 初始化值;

局部常量:

  1. 含义:被final修饰的局部变量
  2. 局部常量的权限访问级别只能是“缺省”

    public static void main(String[] args) {
     //直接声明初始化
     final int num1 = 3;
     //先声明后初始化
     final int num2;
     num2 = 4;
     System.out.println("=====================");
     //直接进行初始化,会被JVM认为是字面值常量
     final byte b1 = 3;
     final byte b2 = 4;
     byte b3 = b1 + b2;
     //先声明后初始化,进行数学运算还是会自动提升为int类型数据
     //以下代码编译错误
     final byte b1;
     b1 = 3;
     final byte b2;
     b2 = 4;
     byte b3 = b1 + b2;
    }
    

    实例常量

  3. 含义:被final修饰的实例变量

  4. 堆内存在识别实例变量被final修饰后,不会给其进行默认值赋值操作
  5. 实例常量如果进行先声明后初始化操作,需要在实例常量所在类的所有构造器给其进行赋值操作
    1. 如果所有的构造器中初始化赋值的数据相同,直接在构造器代码块中进行简化操作
    2. 如果所有的构造器中初始化赋值的数据不同,只能在各自的构造器中进行赋值操作

      静态常量

  • static final 修饰的变量成为“静态常量”
  • 常量名建议全部大写,每个单词之间采用下环线进行衔接
  • 堆内存在识别静态变量被final修饰后,不会给其进行默认值赋值操作
  • 静态常量如果进行先声明后初始化操作,需要在静态常量所在类的静态代码块中给其进行赋值操作

常量(static final)和静态变量(static)的区别

  • 常量的值不能改变,静态变量的值可以改变
  • 常量和静态变量都是存储在方法区,并且都是类加载的时候进行初始化
  • 常量一般都是公开的。public的

    抽象类

    类到对象是实例,对象到类是抽象

    什么是抽象类

  • 类和类之间有共同特征,将这些共同特征提取出来,形成的就是抽象类,类本身是不存在的,所以抽象类无法创建对象

  • 抽象类属于引用数据类型
  • 语法:
    • 【修饰符列表】 abstract class 类名{}
  • 抽象类是用来继承的
  • 抽象类虽然无法实例化,但是抽象类有构造方法,这个构造方法是供子类使用的

    什么是抽象方法

  • 抽象方法表示没有实现的方法,没有方法体的方法

    • 例如:public abstract viod doSome();
  • 抽象方法的特点:
    • 没有方法体,以分号结尾
    • 前面修饰符有abstract关键字

抽象类中不一定有抽象方法,但是抽象方法必须出现在抽象类中


方法重写

一个非抽象的类继承抽象类,必须将抽象类中的抽象方法进行重写

方法重写的前提条件

  1. 必须有字父类继承关系或实现关系
  2. 子类重写方法的方法名必须和父类方法的名相同
  3. 子类重写方法的形参列表必须和父类方法的形参列表相同
  4. 子类重写方法的权限访问级别必须大于或等于父类方法的权限访问级别
  5. 子类重写方法的返回值类型在内存中必须和父类方法的返回类型相同

方法重写的注意事项

  1. 子类重写父类方法的时候,有些情况不满足前提条件,代码也不会报错,这就成了方法的重载,Java提供了一种用于检测是否满足方法重写的语法规则,格式:@Override (在子类重写方法上)
  2. 父类中的私有方法不能被子类重写
  3. 父类和子类不在同一个包下,父类的缺省方法不可以被子类进行重写
  4. 父类中的静态方法不能被子类进行重写,但子类的静态方法代码必须遵循方法重写的语法格式
  5. 父类的最终方法不能被子类进行重写
  6. 父类的抽象方法必须被子类进行重写
  7. 子类重写方法的返回类型在代码中,

    1. 当父类被重写方法的返回类型是void时,必须和父类被重写方法的返回类型相同
    2. 当父类被重写方法的返回类型是基本类型时,必须和父类被重写方法的返回类型相同
    3. 当父类被重写方法的返回类型是引用类型时,必须和父类被重写方法的返回类型形同或者是该返回值类型的子类型

      接口

      接口的基础语法

  8. 接口也是一种数据类型

  9. 接口和接口之间可以多继承
  10. 接口怎么定义:[修饰符列表] interface 接口名{}
  11. 接口编译之后也是class字节码文件,支持多继承
  12. 接口中有
    1. 静态常量:接口中的静态常量只能直接声明初始化
    2. 抽象方法
    3. 默认方法
      1. JDK8.0 接口中可以有默认方法
      2. 接口无法实例化对象,需要在接口中将实例方法修改为默认方法
      3. 创建实现类对象后,可以 实现类对象.默认方法名 调用接口中的默认方法
      4. 当实现类实现了多个父接口,多个父接口中存在相同的默认方法,子类必须以实例方法重写该同名的默认方法
    4. 静态方法: 实现类不可以继承父接口的静态方法
    5. 私有方法
  13. 接口中所有的元素都是public修饰的
  14. 接口中抽象方法的public abstract可以省略(缺少任何一个都可以)
  15. 接口中常量的public static final 可以省略(缺少任何一个都可以)
  16. 接口中方法不能有方法体
  17. 当一个非抽象类实现接口的话,必须将接口中所有的抽象方法全部实现实现使用implements关键字完成
  18. 一个类可以同时实现多个接口

    interface Flyable{
     void fly();
    }
    class Animal{
    }
    class Cat extends Animal implement Flyable{
     public void fly(){
     };
    }
    

    当一个类同时又继承和实现接口时,继承在前,实现接口在后

    接口在开发中的作用

    面向接口编程,可以降低程序的耦合度,提高程序的扩展力,符合ocp原则
    以后进行大项目的开发,一般都是将项目分成一个模块一个模块,模块和模块之间采用接口进行衔接,降低程序的耦合度。
    任何一个接口都有调用者和实现者,接口可以将调用者和实现者解耦合。

    public class Customer {
     private Menu m;
    
     public Customer() {
     }
    
     public Customer(Menu m) {
         this.m = m;
     }
    
     public Menu getM() {
         return m;
     }
    
     public void setM(Menu m) {
         this.m = m;
     }
     public void order(){
         m.xiHongShiJiDan();
         m.yuXiangRouSi();
     }
    }
    
    public interface Menu {
     void xiHongShiJiDan();
     void yuXiangRouSi();
    }
    public class China implements Menu{
     @Override
     public void xiHongShiJiDan() {
         System.out.println("中餐西红柿炒鸡蛋");
     }
    
     @Override
     public void yuXiangRouSi() {
         System.out.println("中餐鱼香肉丝");
     }
    }
    public class America implements Menu{
     public void xiHongShiJiDan() {
         System.out.println("西餐西红柿炒鸡蛋");
     }
    
     @Override
     public void yuXiangRouSi() {
         System.out.println("西餐鱼香肉丝");
     }
    }
    
    public class Eat {
     public static void main(String[] args) {
    //        Menu m = new China();
         Menu m =new America();
         Customer c = new Customer(m);
         c.order();
     }
    }
    

    类型和类之间的关系

  19. is a:

    1. Cat is a Animal (猫是一个动物)

凡是能满足is a 的表示“继承关系”
A extends B

  1. has a:
    1. I has a Pen(我有一支钢笔)

凡是能满足has a关系的表示“关联关系”
关联关系通常以“属性”的形式存在
A {
B b;
}

  1. like a :
    1. Cooker like a FoodMenu(厨师像一个餐单一样)

凡是能满足like a 关系的表示“实现关系”
实现关系通常满足:类实现接口
A implements B

抽象类和接口有什么区别

  1. 抽象类是半抽象的,接口是完全抽象的
  2. 抽象类中有构造方法,接口中没有构造方法
  3. 接口和接口之间支持多继承,类和类之间只能单继承
  4. 一个类可以同时实现多个接口,一个抽象类只能继承一个类(单继承)
  5. 接口中只允许出现常量和 抽象方法

    package

    为什么要使用package

  • package是Java中包机制,包机制的作用是为了方便程序的管理,不同功能的类分别存放在不同的包下

    package程序怎么编译和运行?

  1. 编译:Javac -d . com,bjpowernode.HelloWord
    1. javac 负责编译的命令
    2. -d 带包编译
    3. . 表示编译之后放到当前目录下
  2. 运行: java com.bipowernode.HelloWorld (打包过后类名变为com,bjpowernode.HelloWord,不再是Hello World)

    import

    import什么时候用?

  • A类使用B类
  • A类和B类在同一个包下,不需要使用import
  • A类和B类不在同一个包下,需要使用import
  • Java.lang.* 这个包下的类不需要使用import导入

    import怎么用?

  • import语句只能出现在package语句下,class声明语句上

  • import语句还可以采用星号的方式(效率不低,编译器在编译的时候,会自动把变成具体的类名) 只是一个类名,不能import *

    访问控制权限

    | 访问控制修饰符 | 本类 | 同包 | 不同包的子类 | 任意位置 | | —- | —- | —- | —- | —- | | public | 可以 | 可以 | 可以 | 可以 | | protected | 可以 | 可以 | 可以 | 不可以 | | 缺省 | 可以 | 可以 | 不可以 | 不可以 | | private | 可以 | 不可以 | 不可以 | 不可以 |

什么是API?

  1. 应用程序编程接口(Application Program Interface)
  2. 整个JDK的类库就是一个javase的API
  3. 每一个API都会配置一套API帮助文档
  4. SUN公司提前写好的这套类库就是API(一般每一份API都对应一份API帮助文档)

    内部类

    什么是内部类

  • 在类的内部又定义了一个新的类,被称为内部类

    内部类的分类

  1. 静态内部类:类似于静态变量
  2. 实例内部类:类似于实例变量
  3. 局部内部类:类似于局部变量

    匿名内部类

  • 接口可以用匿名内部类的方法直接实例化

    public class Test {
     public static void main(String[] args) {
         MyMath mm = new MyMath();
         System.out.println(mm.sum(new Compute(){//后面的{}代表了对接口的实现
             public int sum(int a, int b){
                 return a+b;
             }
         }, 2, 2));
    
     }
    }
    interface Compute{
     int sum(int a, int b);
    }
    /*class ComputerImpl implements Compute{
     public int sum(int a, int b){
         return a+b;
     }
    }
    */
    class MyMath {
     public int sum(Compute c, int a, int b) {
         return a + b;
     }
    }
    
  • 一个类没有名字,没有办法重复使用,另外代码太乱,可读性太差

String 类

  1. String 表示字符串类型,属于引用数据类型,不属于基本数据类型
  2. 在Java中随便用双引号括起来的都是String对象
  3. 在Java中规定,双引号括起来的字符串都是不可变的,因为底层封装了一个被final修饰的数组
  4. 在JDK当中双引号括起来的字符串,都是直接存储在“方法区”的“字符串常量池”当中的
  5. “”,字面值常量,字符串对象 null,字面值常量,引用类型的默认值
  6. String字符串拼接在底层实际上是创建StringBuilder对象进行拼接

    为什么Sun公司把字符串存储在一个“字符串常量池”当中

  • 因为字符串在实际的开发中使用太频繁,为了执行效率,所以把字符串放到了方法去的字符串常量池当中

字符串JVM内存图

public static void main(String[] args){
    String s1 = "hello";
    String s2 = "hello" + "xyz";
    String x = new String("xyz")'
}

875003430d98681ec347544f5cfb208.jpg

  • s变量中保存的不是字符串对象的内存地址,s引用保存的是0x1111,0x1111指向”hello”字符串对象在字符串常量池当中的内存地址


字符串底层数组的数据类型

  1. JDk8.0(包含)以前:char[] JDK9.0(包含)以后:byte[]
  2. 为什么将char[]的底层数组修改为byte[]的底层数组?

    1. 时间复杂度
      1. 如果底层数组是char[],数组中元素类型是char,进行底层操作时,先将char转换byte,再由byte转换成二进制位
      2. 如果底层数组是byte[],数组中元素类型是byte,进行底层操作时,直接将byte转换成二进制位
    2. 空间复杂度
      1. 底层数组是char[],会根据内码的存储规则进行存储,
      2. 当存储字符串内容是”abc”时,每个字符根据内码规则占2个字节,总共3个字符,共占内存6个字节
      3. 当存储字符串内容是”尚硅谷”时,每个字符根据内码规则占2个字节,总共3个字符,共占内存6个字节
      4. 底层数组是byte[],会根据开发环境的编码规则进行存储,
      5. 如果是GBK编码,当存储字符串内容是”abc”时,每个英文字符占2个字节,总共3个字符,共占内存6个字节
      6. 如果是GBK编码,当存储字符串内容是”尚硅谷”时,每个汉字字符占2个字节,总共3个字符,共占内存6个字节
      7. 如果是utf8编码,当存储字符串内容是”abc”时,每个英文字符占1个字节,总共3个字符,共占内存3个字节
      8. 如果是utf8编码,当存储字符串内容是”尚硅谷”时,每个汉字字符占3个字节,总共3个字符,共占内存9个字节

        基本类型数据和字符串类型数据的转换

  3. 将基本类型数据转换成字符串类型数据的方式:

    1. 字符串连接符
    2. 先进行装箱,再调用包装类型的实例方法toString()
    3. 包装类型的静态方法toString(基本类型数据)
    4. String类的静态方法ValueOf(基本类型数据)(推荐)
  4. 将字符串类型数据转换成基本类型数据的方式:
    1. 包装类型的构造器,再进行拆箱
    2. 包装类型的静态方法parseXxx(字符串数据)(推荐) 备注:Xxx为对应实例的基本类型单词
  5. 注意事项:

    1. 将字符串类型数据转换成基本类型数据时,字符串中的内容必须在该基本类型的数据范围内,否则发生数据格式化异常(NumberFormatException)
    2. Character类并没有提供将字符串类型数据转换成char类型数据的方式
      public static void main(String[] args) {
      //基本==>字符串1:字符串连接符
      String str1 = 123 + "";
      //基本==>字符串2:进行装箱,在调用包装类型的实例方法toString()
      String str2 = new Integer(234).toString();
      //基本==>字符串3:包装类型的静态方法toString(基本类型数据)
      String str3 = Integer.toString(345);
      //基本==>字符串4:String类的静态方法valueOf(基本类型数据)
      String str4 = String.valueOf(456);
      System.out.println("====================");
      //字符串==>基本1:包装类型的构造器,在进行拆箱
      int num1 = new Integer("567");
      //字符串==>基本2:包装类型的静态方法parseXxx(字符串数据)
      int num2 = Integer.parseInt("678");
      }
      

      Scanner类

      Scanner类基本语法

  6. 针对基本类型数据和字符串类型数据进行简单的扫描的工具类

  7. 构造器
    1. 格式:public Scanner(InputStream source)构造一个新的 Scanner,它生成的值是从指定的输入流扫描的
    2. 实参:System.in
  8. 类方法

    1. public void close() 关闭此扫描器。
    2. public byte nextXxx() 将输入信息的下一个标记扫描为一个 xxx基本类型的数据。
    3. public String next() 查找并返回来自此扫描器的下一个完整标记
    4. public String nextLine() 此扫描器执行当前行,并返回跳过的输入信息。

      Scanner类的注意事项

  9. Scanner类没有提供关于扫描char类型数据的方法

  10. Scanner类扫描键盘录入的数据,录入的数据必须再取值范围以内,否则运行报错
  11. Scanner类的next()和扫描基本类型数据的方法遇到“空白符号”停止扫描,执行代码或进入下次扫描

    Scanner sc = new Scanner(System.in);
    System.out.println("请输入一个数字");
    int i = sc.nextInt();
    System.out.println("i = " + i);
    System.out.println("请输入一个字符串");
    String s = sc.next();
    System.out.println("s = " + s);
    sc.close();
    

    代码执行结果
    屏幕截图(5).png

  12. Scanner类的next()和扫描基本类型数据的方法遇到“回车换行”不会扫描

  13. Scanner类的nextLine()遇到“回车换行”停止扫描
  14. 使用Scanner类的nextLine()时,需要保证整个方法调用之前,不能使用next()和扫描基本类型数据的方法
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入一个数字");
    int i = sc.nextInt();
    System.out.println("i = " + i);
    System.out.println("请输入一行");
    String s = sc.nextLine();
    System.out.println("s = " + s);
    System.out.println("请输入一个字符串");
    String s1 = sc.next();
    System.out.println("s1 = " + s1);
    sc.close();
    
    代码执行结果
    屏幕截图(6).png

包装类

  1. 含义:Java中针对8种基本类型提供对应的引用数据类型
  2. 分类:

    1. 整数型: Byte(byte)、Short(short)、Integer(int)、Long(long)
    2. 浮点型:Float(float)、Double(double)
    3. 布尔型:Boolean(boolean)
    4. 字符型:Character(char)

      拆箱和装箱

  3. 拆箱和装箱

    1. 拆箱:将包装类型数据转换成基本类型数据
    2. 装箱:将基本类型数据转换成包装类型数据
  4. 装箱的方式:
    1. 包装类型的构造器
    2. 包装类型的静态方法valueOf(基本类型数据)
    3. 自动装箱(JDK5.0)推荐:由JVM隐式调用valueOf() 完成装箱的过程
  5. 拆箱的方式

    1. 包装类型的实例方法xxxValue() (xxx为对应实例的基本类型单词)
    2. 推荐:自动拆箱(JDK5.0) 由JVM隐式调用xxxValue() 完成拆箱的过程
      public static void main(String[] args) {
        //装箱1:包装类型的构造器
        Integer num1 = new Integer(123);
        System.out.println(num1);
        //装箱2:包装类型的静态方法valueOf(基本类型数据)
        Integer num2 = Integer.valueOf(234);
        System.out.println(num2);
        //装箱3:自动装箱
        Integer num3 = 345;
        System.out.println(num3);
        System.out.println("=====================================");
        //拆箱1:包装类型的实例方法xxxValue()
        int num4 = new Integer(456).intValue();
        System.out.println(num4);
        //拆箱2:自动拆箱
        int num5 = new Integer(567);
        System.out.println(num5);
      }
      

      StringBuffer类和StringBuilder类

  6. 相同点:

    1. 二者拥有相同的父类
    2. 与String不同,二者是可变的字符序列
  7. 不同点:
    1. 线程安全性不同
      1. StringBuffered类是线程安全的,适用于多线程程序,如果在单线程中进行使用,相比StringBuilder类低很多
      2. StringBuilder类是线程不安全的,适用于单线程程序,如果在多线程中进行使用,需要手动添加线程安全
  8. StringBuilder类

    1. 一个可变的字符序列,因为底层数组没有被final修饰
    2. 类方法
      1. public StringBuilder append(String str)
        将指定的字符串追加到此字符序列
      2. public StringBuilder insert(int offset,String str)
        将字符串插入此字符序列
      3. public StringBuilder reverse()
        将此字符序列用其反转形式取代。
        public static void main(String[] args) {
        String str = "";
        //在结尾处进行追加字符串
        str.concat("HelloWorld");
        System.out.println(str);
        StringBuilder sb = new StringBuilder();
        //在结尾处进行追加字符串
        sb.append("HelloWorld");
        System.out.println(sb);
        //在指定的索引位置插入指定的字符串
        sb.insert(5,"Java");
        System.out.println(sb);
        //将指定的字符串进行反转
        sb.reverse();
        System.out.println(sb);
        }
        //代码执行结果
        HelloWorld
        HelloJavaWorld
        dlroWavaJolleH
        

        StringBuilder类源码分析

  9. StringBuilder类底层数组的初始容量是多少
    初始容量是多少取决于创建对象时所选择的构造器
    (1)StringBuilder()
    初始容量:16
    (2)StringBuilder(CharSequence seq)
    初始容量:参数长度+16
    (3)StringBuilder(int capacity)
    初始容量:自定义
    (4)StringBuilder(String str)
    初始容量:参数长度+16

  10. StringBuilder类底层数组的扩容原理(规则)
    JDK8.0(包含)以后:
    (原来数组长度 << 1 ) + 2
    JDK7.0(包含):
    原来数组长度 2 + 2;
    JDK6.0(包含)以前:
    (原来数组长度 + 1)
    2;
  11. StringBuilder类底层数组扩容的时候,为什么+2?
    为了防止通过自定义初始容量数组传0的特殊情况

日期相关类

获取当前时间,并格式化日期对象

public static void main (String[] args){
    //获取当前时间
    Date date = new Date();
    //格式化日期格式
    DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //调用方法获取日期字符串
    String s = sdf.format(date);
    System.out.println(s);
}
public static void main (String[] args){
    //获取时间字符串
    String str = "2022-06-17 09:33:12";
    //创建DateFormat对象
    DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //调用parse方法转换为日期对象,返回值为Date对象
    Date date = sdf.parse(str);
    System.out.println(date);
}