【JAVA入门】Day16 - 内部类
文章目录
类的五大成员:属性、方法、构造方法、代码块、内部类。
内部类,顾名思义,就是在一个类的里面,再定义一个类。
如下:在 A类 的内部定义 B类,B类 就被称为内部类。
public class Outer { //外部类
public class Inner { //内部类
}
}
public class Test { //外部其他类
public static void main(String[] args) {
}
}
一、为什么要有内部类
举个例子。
需求:写一个 Javabean 类描述汽车。
属性:汽车的品牌、年龄、颜色、发动机的品牌、发动机使用年限。
这个例子下,发动机有自己的品牌和使用年限,它和汽车是不同的个体,但是又依赖于车,离开了车,发动机也没有意义。此时我们称之为依赖的同时又保持自身的部分独立性。这个时候,我们就可以使用内部类,把发动机定义为一个内部类。
public class Car { //外部类
String carName;
int carAge;
int carColor;
class Engine { //内部类
String engineName;
int engineAge;
}
}
- 内部类表示的事物是外部类的一部分。
- 内部类单独出现没有任何意义。
- 内部类可以直接访问外部类的成员,包括私有成员。
- 外部类要访问内部类的成员,必须创建对象。
二、内部类的分类
内部类主要有四种:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
2.1 成员内部类
写在成员位置的,属于外部类的成员。
public class Car { //外部类
String carName;
int carAge;
int carColor;
class Engine { //成员内部类
String engineName;
int engineAge;
}
}
2.1.1 成员内部类可以被权限修饰符修饰
成员内部类也是可以被一些修饰符所修饰的,比如:private,默认,protected,public,static 等。
public class Car { //外部类
String carName;
int carAge;
int carColor;
private class Engine { //成员内部类
String engineName;
int engineAge;
}
}
2.1.2 如何获取成员内部类的对象
如果想获取成员内部类的对象,有两种方式:
- 在外部类中编写方法,对外提供内部类的对象。
- 直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
范例:Outer.Inner oi = new Outer().new Inner();
由于内部类可以用 private 修饰,此时内部类将会变成私有的,那么就不能在外界随意创建他的对象,即Outer.Inner oi = new Outer().new Inner(); 会报错,因此第一种调用方法就出现了,可以利用外部类编写方法,对外提供内部类的对象。
public class Outer {
String name;
private class Inner {
}
//获取一个内部类对象
public Inner getInstance() {
return new Inner();
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
//Outer.Inner oi = new Outer().new Inner();报错
//因为 Inner 私有,所以不能直接写 Outer.Inner
//可以利用Object类声明接受(Object是所有类的公有父类)
//这里是一种多态(Object是Inner的父类,子类对象赋值给父类,是为多态)
Object ob = o.getInstance();
System.out.println(ob);// InnerClass.Outer$Inner@2133c8f8
}
}
如果内部类不用 private 修饰,而是默认或用 public 修饰,那么内部类就可以直接在外界被访问,此时可以使用第二种调用方法。
public class Outer {
private String name;
public class Inner {
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
Outer.Inner oi = new Outer().new Inner();
System.out.println(oi);// InnerClass.Outer$Inner@2133c8f8
}
}
2.1.3 JDK16 后新增的成员内部类里可以定义静态变量
在成员内部类里,JDK16 之前不能定义静态变量,JDK16 开始才可以定义静态变量。
public class Inner {
static int a = 10;
}
2.1.4 成员内部类如何获取外部类的成员变量
成员内部类获取外部类成员变量,复杂的是重名的情况(如果不重名,各写各的名字即可),这个时候仍采取的是就近原则。
class Outer {
private int a = 10;
class Inner {
private int a = 20;
public void show() {
int a = 30;
System.out.println(Outer.this.a); //10
System.out.println(this.a); //20
System.out.println(a); //30
}
}
}
2.2 静态内部类
静态内部类就是用 static 修饰的内部类。
public class Car { //外部类
String carName;
int carAge;
int carColor;
static class Engine { //静态内部类
String engineName;
int engineAge;
}
}
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。
创建静态内部类对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
静态内部类调用非静态方法的格式:
先创建对象,用对象调用。
静态内部类调用静态方法的格式:
外部类名.内部类名.方法名();
如下代码所示,show1() 是非静态方法,show2() 是静态方法。
要想在内部类中访问外部类的静态变量,直接调用访问即可;但要想访问外部类种的非静态方法,就要先创建一个外部类的对象,再用对象调用这个非静态变量。
public class Outer {
int a = 10;
static int b = 20;
//静态内部类
static class Inner {
public void show1() {
Outer o = new Outer();
System.out.println(o.a);
System.out.println(b);
System.out.println("内部类的非静态方法被调用了");
}
public static void show2() {
Outer o = new Outer();
System.out.println(o.a);
System.out.println(b);
System.out.println("内部类的静态方法被调用了");
}
}
}
在测试类当中,如果想要访问静态内部类中的静态方法 show2(),直接用类名点上方法即可(只要是静态的东西,都可以直接用类名获取);但是如果想访问静态内部类中的非静态方法 show1(),需要先创建这个内部类的对象,再用对象调用 show1()。
public class Test {
public static void main(String[] args) {
Outer.Inner oi = new Outer.Inner();
oi.show1();
Outer.Inner.show2();
}
}
由此可知,凡是静态相关的东西,都是用类名直接调用;凡是非静态的东西,都是用对象调用。
3.1 局部内部类
1.将内部类定义在方法里面,就叫局部内部类,类似于方法里的局部变量。
2.外界无法使用局部内部类,需要在方法内部创建对象并使用。
3.该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
public class Outer {
int b = 20;
public void show() {
int a = 10;
//局部内部类
class Inner{
String name;
int age;
public void method1(){
System.out.println(a);
System.out.println(b);
System.out.println("局部内部类中的method1方法");
}
public static void method2(){
System.out.println("局部内部类中的method2方法");
}
}
//创建局部内部类的对象
Inner i = new Inner();
System.out.println(i.name);
System.out.println(i.age);
i.method1();
Inner.method2();
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
o.show();
}
}
3.2 匿名内部类
匿名内部类本质上是隐藏了名字的内部类。
匿名内部类既可以作为成员内部类,也可以作为局部内部类存在。
格式:
new 类名或接口名() {
重写方法;
};
举例:
new Inter() {
public void show() {
}
};
new Inter 后面的 { },实质上是类的内容,是一个类,这个类本身是没有名字的,所以叫匿名内部类,而这个类实现了 Inter 这个接口,因此需要在其内部重写接口里所有的抽象方法。而 new 就是创建了这个没有名字的类的对象。
举例2:
public abstract class Animal {
public abstract void eat();
}
public class Test {
public static void main(String[] args) {
new Animal() {
@Override
public void eat() {
System.out.println("重写了eat方法");
}
};
}
}
此时的匿名内部类起到的作用是继承,继承的是前面的 Animal 类,并对 Animal 里的所有抽象方法进行了实现。
匿名内部类的诞生其实也是为了简化代码,将一次实现/继承生成的对象作为参数,传递给某个方法,大大减少了代码量,优化了很多步骤。
public class Test {
public static void main(String[] args) {
method(
new Animal() {
@Override
public void eat() {
System.out.println("重写了eat方法");
}
}
); //用一个匿名内部类生成的对象作为参数传递给method方法
}
public static void method(Animal a) { //将匿名内部类传递给a,在匿名内部类中重写eat方法,实现了一个多态
a.eat();
}
}
匿名内部类生成的对象,也可以采用链式编程,调用自己内部的所有方法:
new Swim() {
@Override
public void swim() {
System.out.println("重写游泳方法");
}
}.swim();
匿名内部类还可以实现接口的多态:
//接口多态
Swim s = new Swim(){ //接口创建的对象被匿名内部类实现了
@Override
public void swim() {
System.out.println("重写的游泳方法");
}
};
s.swim();