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
接口 -
浅克隆
拷贝出的新对象,与原对象中的数据一模一样(引用类型拷贝的只是地址)
-
深克隆
- 对象中基本类型的数据直接拷贝
- 对象中的字符串数据拷贝的还是地址
- 对象中包含的其他对象,不会拷贝地址,会创建新对象
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 包装类
- 包装类就是把基本类型的数据包装成对象
- 自动装箱:基本数据类型可以自动转换为包装类型
- 自动拆箱:包装类型可以自动转换为基本数据类型
基本数据类型 | 对应的包装类(引用数据类型) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
- 把基本类型的数据转换成字符串类型
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() | 返回一个字符串(该字符串就是拼接之后的结果) |