内部类
概念
- 是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类。
场景
- 当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类。
分类
-
成员内部类(了解)
-
类中的一个普通成员
- 内部类中构造器,成员变量和方法都可以定义,与普通类一样
- 成员内部类可以直接访问外部类的静态成员和实例成员,不需要new对象
- 外部类名.this.外部类成员 可以访问外部类的成员
- 创建成员内部类对象:new 外部类( ).new 内部类( )
-
- 静态内部类(了解)
- 有static修饰的内部类,属于外部类自己持有
- 类有的内容,静态内部类都可以有
- 只能直接访问外部类的静态成员,但是不能直接访问外部类的实例成员,需要new对象
- 因为同静态方法一样,直接属于类,而外部类的实例成员是属于对象,每个对象内容不同
- 创建静态内部类对象:new 外部类.new 内部类( )
局部内部类(了解)
-
定义在方法中、代码块中、构造器等执行体中
匿名内部类(重点)
-
一种特殊的局部内部类,不需要为这个类声明名字
-
特点
- 匿名内部类本质就是一个子类,并会立即创建出一个子类对象。
-
作用
- 用于更方便的创建一个子类对象。
//使用匿名内部类
public class Tets {
public static void main(String[] args) {
// 匿名内部类
//匿名内部类的本质是继承类或者实现接口
//下面的代码会定义一个继承了Fu类的匿名内部类并且创建该子类对象
Fu f = new Fu(){
@Override
public void show() {
System.out.println("Son show");
}
};
f.show();
}
}
class Fu{
public void show(){
System.out.println("Fu show");
}
}
/*
result:
Son show
*/
常见应用场景
-
通常作为一个参数传输给方法
- 在开发中一般都不是我们主动用,而是在调用API时,根据API需要的参数被动使用
//简单应用
public class Test {
public static void main(String[] args) {
//下面两个代码把匿名内部类直接作为参数传入
//等价于创建两个类实现Fly接口并分别创建对象,并将对象作为参数传入
showFly(new Fly() {
@Override
public void fly() {
System.out.println("鸟飞了");
}
});
showFly(new Fly() {
@Override
public void fly() {
System.out.println("我飞了");
}
});
}
public static void showFly(Fly f) {
f.fly();
}
}
interface Fly {
void fly();
}
枚举
枚举是一种特殊类
特点
- 枚举类中的第一行,只能写一些合法的标识符(名称),多个名称用逗号隔开。
- 每个名称记住的都是枚举类的一个对象,默认使用public static final修饰,本质为常量
- 枚举类中,从第二行开始,可以定义类的其他各种成员。
- 枚举类的构造器都是默认私有,因此枚举类对外不能创建对象。
- 枚举都是最终类,默认使用final修饰,不可以被继承。
- 编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法。
public enum A {
// 枚举对象,必须放在第一行,表示A类的三个实例对象的名字,都为常量,使用public static final修饰
X,Y,Z;
public static String name;
private int a;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public void say(){
System.out.println("成员方法");
}
}
public class Test {
public static void main(String[] args) {
A.name = "静态类变量";
System.out.println(A.name);//输出类变量
//X,Y,Z都是A类的实例对象,都是常量,使用public static final修饰
A a1 = A.X;
System.out.println(a1);//输出a1所指向的枚举类中的对应对象
A a2 = A.Y;
System.out.println(a2);//输出a1所指向的枚举类中的对应对象
// 枚举类型的构造器私有,无法对外创建对象,默认使用private修饰
// A a2 = new A();
//枚举类包含的额外的API
A[] as = A.values();//获取枚举类中的所有对象
System.out.println(Arrays.toString(as));
for (A a : as) {
System.out.print(a + " ");
}//输出枚举类中的所有对象
System.out.println();
A a3 = A.valueOf("Z");
System.out.println(a3.name());//输出a3所指向的枚举类中的对应对象的名字
System.out.println(a3.ordinal());//输出a3所指向的枚举类中的对应对象的序号
a3.setA(10);
System.out.println(a3.getA());//输出a3所指向的枚举类中的对应对象的成员变量a的值
a3.say();//输出a3所指向的枚举类中的对应对象的成员方法
}
}
/*
result:
静态类变量
X
Y
[X, Y, Z]
X Y Z
Z
2
10
成员方法
*/
抽象枚举
枚举类中有抽象方法,此时只有每个对象都重写抽象方法后才不会报错
public enum B {
//CAT是一个对象,并且重写枚举类B中抽象方法,使用无参构造
CAT(){
@Override
public void say() {
System.out.println(getName() + "喵喵喵");
//因为这是个对象,所以不能直接用name调用
}
},DOG("小狗"){
@Override
public void say() {
System.out.println(getName() + "汪汪汪");
}
};//DOG也是一个对象,并且重写枚举类B中抽象方法,使用有参构造
B() {
}
B(String name) {
this.name = name;
}
public abstract void say();//抽象方法
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Test1 {
public static void main(String[] args) {
B.CAT.setName("小猫");
B.CAT.say();//因为CAT是枚举类中常量对象,所以两次的B.CAT都是同一个对象
B.DOG.say();
}
}
/*
result:
小猫喵喵喵
小狗汪汪汪
*/
泛型
定义类,接口,方法时,声明一个或多个类型变量,成为泛型类,泛型接口和泛型方法,统称为泛型
- 作用
泛型提供了在编译阶段 约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常,保证所操作的数据类型的统一。
- 本质
将具体的数据类型作为参数传给泛型变量,也就是操作阶段不需要知道具体数据类型,只需要操作泛型编写,最后通过传入的具体的数据类型来替换泛型变量,自动操作
- 泛型类
通过定义泛型类使泛型类中的成员可以通过传入的不同的泛型执行相同的操作
格式:
修饰符 class 类名<类型变量,类型变量, ...>{
}
类型变量常用大写的英文字母,常用的有:E、T、K、V等
泛型类:
泛型接口和泛型类 类似,也可以同样可以单泛型,多泛型与继承泛型
泛型方法:
格式:
修饰符<类型变量,类型变量,...> 返回值类型 方法名(形参){
}
//这是泛型方法
public <E> void test(E e){
}
例子:
public class People {
}
public class Teacher extends People{
}
public class Student extends People{
}
public class Test {
public static void main(String[] args) {
test1("单泛型方法");
test2("多泛型方法", "方法");
//只要是People的子类或者People类都可以传入
test3(new People());
test3(new Student());
test3(new Teacher());
}
public static <T> void test1(T t) {
System.out.println(t);
}
public static <T,E> void test2(T t,E e) {
System.out.print(t);
System.out.println(e);
}
public static <T extends People> void test3(T t) {
System.out.println("=======上限泛型方法======");
System.out.println(t);
}
}
/*
result:
单泛型方法
多泛型方法方法
=======泛型方法上限======
com.LMH.Fanxingmethod.People@1b6d3586
=======泛型方法上限======
com.LMH.Fanxingmethod.Student@4554617c
=======泛型方法上限======
com.LMH.Fanxingmethod.Teacher@74a14482
*/
通配符和泛型上下限
泛型的注意事项
泛型擦涂
- 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了
- 泛型不支持基本数据类型,只能支持引用数据类型
public class Test3 {
public static void main(String[] args) {
// ArrayList<int> list1 = new ArrayList<>();
// 会报错,因为泛型只能是引用类型,不能是基本类型
ArrayList<Integer> list2 = new ArrayList<>();
}
}