day03_内部类、枚举、泛型、API

66 阅读8分钟

day03_内部类、枚举、泛型、API

1. 内部类

  • 内部类是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类。
  • 当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类。

1.1 成员内部类【了解】

  • 成员内部类就是类中的一个普通成员,类似前面我们学过的普通的成员变量、成员方法

    public class Outer {
        int a = 20;
        // 成员内部类
        public class Inner {
            int a = 10;
            public void innerMethod() {
                System.out.println(a); // 10
                System.out.println(Outer.this.a); // 20
            }
        }
    }
    
  • 创建对象的格式

    外部类名.内部类名 对象名 = new 外部类名(...).new 内部类名(...);
    
    Outer.Inner oi = new Outer().new Inner();
    
  • 在成员内部类的实例方法中可以直接访问外部类的实例成员、静态成员

  • 在成员内部类的实例方法中可以拿到当前外部类对象,格式是:外部类名.this

1.2 静态内部类【了解】

  • 静态内部类有static修饰的内部类,属于外部类自己持有

    public class Outer {
        // 静态内部类
        public static class Inner {
            
        }
    }
    
  • 创建对象的格式

    外部类名.内部类名 对象名 = new 外部类名(...).内部类名(...);
    
    Outer.Inner oi = new Outer.Inner();
    
  • 在静态内部类中可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员

1.3 局部内部类【鸡肋】

  • 局部内部类是定义在在方法中、代码块中、构造器等执行体中

    public class Test {
        public static void main(String[] args) {
            
        }
        
        public static void go() {
            class A {}
            
            abstract class B {}
            
            interface C {}
        }
    }
    

1.4 匿名内部类

1.4.1 匿名内部类概述

  • 匿名内部类就是一种特殊的局部内部类;所谓匿名:指的是程序员不需要为这个类声明名字

    new 类或接口(形参列表) {
        类体(一般是方法重写);
    }
    
  • 用于更方便的创建一个子类对象,因为匿名内部类本质就是一个子类,并会立即创建出一个子类对象

  • 匿名内部类通常作为一个参数传输给方法

1.4.2 案例演示

需求:猫、狗参加游泳比赛

public interface Swimming {
    void swim();
}
public class Test {
    public static void main(String[] args) {
        Swimming s = new Swimmimg() {
            @Override
            public void swim() {
                System.out.println("狗游泳很快很快~~");
            }
        };
        go(s);
        
        go(new Swimming() {
           @Override
            public void swim() {
                System.out.println("猫游泳还行");
            }
        });
    }
    
    public static void go(Swimming s) {
        System.out.println("=====start=====");
        s.swim();
    }
}

2. 枚举

2.1 枚举概述

  • 枚举是一种特殊的类

    修饰符 enum 枚举类名 {
        名称1, 名称2, ..., 名称n;
        其它成员...
    }
    
  • 枚举类中的第一行只能写一些合法的标识符(名称),多个名称用逗号隔开

  • 这些名称,本质是常量,每个常量都会记住枚举类的一个对象

  • 默认继承Enum

2.2 枚举类的特点

// 一个枚举类
public enum A {
    X, Y, Z;
}

将其编译后的字节码文件反编译:

Compiled from “A.java"
public final class A extends java.lang.Enum<A> {
    public static final A X = new A();
    public static final A Y = new A();
    public static final A Z = new A();

    public static A[] values();
    public static A valueOf(java.lang.String);
}
  • 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
  • 枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。
  • 枚举都是最终类,不可以被继承。
  • 枚举类中,从第二行开始,可以定义类的其他各种成员。
  • 编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法。

2.3 案例演示

  • Sex.java
public enum Sex {
    MAN, WOMAN
}
  • Test.java
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        System.out.println(Sex.valueOf("MAN")); // MAN
        System.out.println(Sex.valueOf("WOMAN")); // WOMAN
        System.out.println(Arrays.toString(Sex.values())); // [MAN, WOMAN]
    }
}

2.4 用枚举类实现单例设计模式

public class Singleton {
    private Singleton() {
    }

    public enum SingletonEnum {
        SINGLETON;

        private Singleton instance = null;

        private SingletonEnum() {
            instance = new Singleton();
        }

        public Singleton getInstance() {
            return instance;
        }
    }
}
public static void main(String[] args) {
    Singleton s1 = SingletonEnum.SINGLETON.getInstance();
    Singleton s2 = SingletonEnum.SINGLETON.getInstance();
    System.out.println(s1 == s2); // true
}

2.5 枚举类的使用场景

  • 用来表示一组信息,然后作为参数进行传输
    • 参数值不受约束
    • 代码可读性好,参数值得到了约束,对使用者更友好
  • switch高度匹配枚举类
  • 可以用作信息标记

3. 泛型

3.1 泛型概述

  • 定义类、接口、方法时,同时声明了一个或者多个类型变量(如:) ,称为泛型类、泛型接口,泛型方法、它们统称为泛型

    public class ArrayList<E> {
        ...
    }
    
  • 作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。

  • 泛型的本质:把具体的数据类型作为参数传给类型变量

3.2 泛型类与泛型接口

  • 类型变量建议用大写的英文字母,常用的有:E、T、K、V 等
// 泛型类
修饰符 class 类名<类型变量, 类型变量...> {
    
}

// 泛型接口
修饰符 interface 接口名<类型变量, 类型变量...> {
    
}

3.3 泛型方法、通配符、泛型上下限

  • 泛型方法

    修饰符 <类型变量, 类型变量...> 返回值类型 方法名(形参列表) {
        
    }
    
  • 通配符

    就是“?”,可以在“使用泛型”的时候代表一切类型; E T K V 是在定义泛型的时候使用

  • 泛型的上下限

    • 泛型上限:? extends Car :? 能接收的必须是Car或者其子类
    • 泛型下限:? super Car :?能接收的必须是Car或者其父类

3.4 泛型的擦除问题和基本数据类型问题

  • 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除
  • 泛型不支持基本数据类型,只能支持对象类型(引用数据类型)

4. 常用API

API(Application Programming interface) :应用程序编程接口

4.1 Object

4.1.1 常用方法

  • Object类是Java中所有类的祖宗类,因此,Java中所有类的对象都可以直接使用Object类中提供的一些方法

  • Object类的常见方法

    方法名说明作用
    public String toString()返回对象的字符串表示形式用于被子类重写,以便返回对象的具体内容
    public boolean equals(Object o)判断两个对象是否相等用于被子类重写,以便子类自己来定制比较规则
    protected Object clone()对象克隆当某个对象调用这个方法时,这个方法会复制一个一模一样的新对象返回

4.1.2 clone

  • 使用时需要实现Cloneable接口

  • 浅克隆

    拷贝出的新对象,与原对象中的数据一模一样(引用类型拷贝的只是地址)

浅克隆.gif

  • 深克隆

    • 对象中基本类型的数据直接拷贝
    • 对象中的字符串数据拷贝的还是地址
    • 对象中包含的其他对象,不会拷贝地址,会创建新对象

深克隆.gif

4.2 Objects

方法名说明
public static boolean equals(Object a, Object b)先做非空判断,再比较两个对象
public static boolean isNull(Object obj)判断对象是否为null
public static boolean nonNull(Object obj)判断对象是否不为null

4.3 包装类

  • 包装类就是把基本类型的数据包装成对象
  • 自动装箱:基本数据类型可以自动转换为包装类型
  • 自动拆箱:包装类型可以自动转换为基本数据类型
基本数据类型对应的包装类(引用数据类型)
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean
  • 把基本类型的数据转换成字符串类型
    • public static String toString(double d)
    • public String toString()
  • 把字符串类型的数值转换成数值本身对应的数据类型
    • public static int parseInt(String s)
    • public static Integer valueOf(String s)

4.4 StringBuilder和StringBuffer

StringBuilder

  • StringBuilder代表可变字符串对象,相当于是一个容器,它里面装的字符串是可以改变的,就是用来操作字符串的
  • 好处:StringBuilder比String更适合做字符串的修改操作,效率会更高,代码也会更简洁
构造器说明
public StringBuilder()创建一个空白的可变的字符串对象,不包含任何内容
public StringBuilder(String str)创建一个指定字符串内容的可变字符串对象
方法名称说明
public StringBuilder append(任意类型)添加数据并返回StringBuilder对象本身
public StringBuilder reverse()将对象的内容反转
public int length()返回对象内容长度
public String toString()通过toString()就可以实现把StringBuilder转换为String
  • 对于字符串相关的操作,如频繁的拼接、修改等,建议用StringBuilder,效率更高!
  • 注意:如果操作字符串较少,或者不需要操作,以及定义字符串变量,还是建议用String

StringBuffer

  • StringBuffer的用法与StringBuilder是一模一样的
  • 但 StringBuilder是线程不安全的 ,StringBuffer是线程安全的

4.5 StringJoiner

  • JDK8开始才有的,跟StringBuilder一样,也是用来操作字符串的,也可以看成是一个容器,创建之后里面的内容是可变的
  • 好处:不仅能提高字符串的操作效率,并且在有些场景下使用它操作字符串,代码会更简洁
构造器说明
public StringJoiner (间隔符号)创建一个StringJoiner对象,指定拼接时的间隔符号
public StringJoiner (间隔符号, 开始符号, 结束符号)创建一个StringJoiner对象,指定拼接时的间隔符号、开始符号、结束符号
方法名称说明
public StringJoiner add(添加的内容)添加数据,并返回对象本身
public int length()返回长度(字符出现的个数)
public String toString()返回一个字符串(该字符串就是拼接之后的结果)