1、内部类
1、定义:一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class)。
内部类是我们类的第五大成员(属性、方法、构造器、代码快、内部类)。
内部类最大特点就是:可以直接访问私有属性。
class Outer {
class Inner{} // 内部类
}
class Other {} // 外部其它类
1.1、内部类分类
1、定义在外部类局部外置上(比如方法内):
a、局部内部类(有类名)
b、匿名内部类(无类名!!!!)
2、定义在外部类的成员位置上:
a、成员内部类(没有static修饰)
b、静态内部类(有static修饰)
1.1.1、局部内部类:
总结:
1、局部内部类定义在外部类局部位置,通常在方法
2、局部内部类可以直接访问外部类成员
3、局部内部类不能添加访问修饰符,但是可以使用final修饰,以防被继承
4、局部内部类作用域:仅仅在定义他的方法或者代码块中(内部类可以写在代码快中)
5、外部类可以在方法中,创建内部类对象,然后调用内部类方法
6、外部其它类不能访问局部内部类,因为局部内部类类似于局部变量。
7、如果外部类和局部内部类成员重名时,默认遵循就近原则,如果想要访问外部类成员,可以使用(外部类名.this.成员)去访问。
public static void main(String[] args) {
Outer outer = new Outer();
outer.m1();
}
class Outer {
private int n1 = 100;
private int n3 = 200;
private void m2() {
System.out.println("outer方法m2()");
}
public void m1() { // 外部类方法
// 3、局部内部类不能添加访问修饰符,但是可以使用final修饰,以防被继承
// 4、局部内部类作用域:仅仅在定义他的方法或者代码块中(内部类可以写在代码快中)
final class Inner { // 1、局部内部类定义在外部类局部位置,通常在方法
private int n1 = 888;
public void f1() {
System.out.println("n3=" + n3); // 2、局部内部类可以直接访问外部类成员
m2();
System.out.println("n1=" + n1); // 888
System.out.println("外部类的n1=" + outer.this.n1);
}
}
// 5、外部类可以在方法中,创建内部类对象,然后调用内部类方法
Inner inner = new Inner();
inner.f1();
}
}
1.1.2、匿名内部类:
1、本质还是一个类。
2、是一个内部类:定义在外部类的局部位置:如方法中
3、该类没有名字。
4、同时还是一个对象。
// 基本语法
new 类名/接口名(参数列表) {
类体
}
基于接口的匿名内部类:
1、为什么需要基于接口的匿名内部类呢?
对于一些实现接口的类我们只是使用一次,但是创建类又比较麻烦,因此为了简化开发,可以使用匿名内部类。
2、在使用匿名内部类的时候发生了什么?
匿名内部类其实也有名字,只是我们看不见而已,是jdk帮我们创建的:以外部类名+$符号+数字构成
它是使用匿名类帮我们实现了接口,并将匿名类给实例化了。
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
}
class Outer {
private int n1 = 10;
public void method() {
// tiger的编译类型是:IA,编译看左边
// tiger的运行类型是:匿名内部类: Outer$1(名字是外部类名字+$符号)
/*
new IA()做了什么事情呢?
jdk底层是创建了一个类Outer$1实现了接口IA并将该类给实例化,并将该类对象地址返回给了tiger
匿名内部类只能使用一次就不能再次使用
class Outer$1 implements IA {
@Override
public void cry() {
System.out.println("老虎叫");
}
}
*/
IA tiger = new IA() { // 使用匿名内部类
// @Override
public void cry() {
System.out.println("老虎叫");
}
};
tiger.cay();
}
}
interface IA {
public void cry();
}
// 传统方法实现接口:需要再创建一个类,并创建对象来实现。
// 但是如果这些类都只是使用一次,这样创建类过于麻烦,为了简化开发,可以使用匿名内部类:
class Tiger implements IA {
// @Override
public void cry() {
System.out.println("老虎叫");
}
}
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
// 调用方法时:直接传入一个实现了ICaluate接口的匿名内部类,改匿名内部类可以灵活的实现work方法,完成不同的计算任务。
/*
ICaluate iCaluate = new ICalculate() { // 编译类型:ICaluate 运行类型:匿名内部类
@Override
public double work(double n1, double n2) {
retruen n1 + n2;
}
}
*/
cellphone.testWork(new ICalculate() {
@Override
public double work(double n1, double n2) {
retruen n1 + n2;
}
}, 10, 20)
}
interface ICalculate {
public double work(double n1, double n2);
}
class CellPhone {
public void testWork(ICaluate iCaluate, double n1, double n2) {
double result = iCaluate.work(n1, n2);
System.out.println("计算后的结果是:" + result);
}
}
总结:
IA接口里面有一个方法,可以不写方法体;有一个普通类normalClass,里面有一个方法AchieveIa用来实现(调用)接口方法,因此该普通类方法的参数要是接口(IA ia),也可以传入别的参数;主方法main中创建普通类normalClass 的实例对象(new一下),用该实例对象调用自己类的AchieveIa方法,但是该方法传入的参数是重写了接口方法的匿名内部类。
基于类的匿名内部类:就是底层创建匿名类继承类。
class Outer {
public void method() {
/*
class Outer$2 extends Father {
@Override
public void test() {
System.out.println("匿名内部类");
}
}
*/
// father 编译类型为:Father
// father运行类型为:Outer$2
Father father = new Father() {
@Override
public void test() {
System.out.println("匿名内部类的test方法");
}
};
/*
运行看右边:因为匿名类重写了father方法,所以输出的是匿名类的值。
没有重写就是找父类的值。
*/
father.test(); // 输出:匿名内部类的test方法
Animal animal = new Animal() {
@Override
public void eat() {
System.out.println("动物吃骨头);
}
};
animal.eat(); // 输出:动物吃骨头
}
}
class Father { // 普通类
public Father(String name) {
System.out.println("father");
}
}
abstract class Animal { // 抽象类
public void eat();
}
匿名内部类细节:
1、匿名内部类可以调用匿名内部类的方法:
class Outer {
public void method() {
Father father = new Father() {
@Override
public void test() {
System.out.println("匿名内部类的test方法");
}
};
father.test(); // 输出:匿名内部类的test方法
new Father() {
@Override
public void test() {
System.out.println("匿名内部类的test方法");
}
@Override
public void ok(String str) {
super.ok(str); // super调的还是父类Father. 因为匿名类继承了father
}
}.test("jack"); // 支持直接调用方法。因为匿名内部类本身也是返回对象
}
}
class Father { // 普通类
public Father(String name) {
System.out.println("father");
}
public void test() {}
public void ok(String str) {
System.out.println(str);
}
}
2、可以直接访问外部类的所有成员,包括私有的。
3、不能添加访问修饰符,因为他就相当于一个局部变量。
4、作用域:仅仅在定义他的方法或者代码快中。
5、外部其它类不能访问局部内部类,因为局部内部类类似于局部变量。
6、如果外部类和局部内部类成员重名时,默认遵循就近原则,如果想要访问外部类成员,可以使用(外部类名.this.成员)去访问。
3、匿名内部类的最佳实践:
当作实参直接传递,简洁高效。
public static void main(String[] args) {
// 将内部类当作实参直接传递:
f1(new IA() {
@Override
public void show() {
System.out.println("hello word");
}
});
// 传统方法
f1(new Say());
public static void f1(IA ia) { // 该方法形参是接口类型
ia.show(); // 通过形参调用接口方法
}
}
interface IA {
void show();
}
// 传统方法
class Say implements IA {
@Override
public void show() {
System.out.println("hello word");
}
}
巩固练习:
题目要求;
一个bell接口,里面有一个ring方法,一个CellPhone 类,里面有一个alarmClock方法。使用匿名内部类为实参的方法打印:该起床了、该上课了。
public static void main(String[] args) {
CellPhone cellphone = new CellPhon();
// 1、alarmClock方法传递的参数是:实现了Bell接口的匿名内部类
// 2、该类重写了ring方法。
// 3、改匿名内部类传递给了 Bell bell
cellphone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("该起床了。");
}
});
cellphone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("改上课了。");
}
});
}
interface Bell {
void ring();
}
class CellPhone {
/* 4、Bell bell = new Bell() { 编译类型是Bell,运行类型是匿名内部类
@Override
public void ring() {
System.out.println("该起床了。");
}
*/
public void alarmClock(Bell bell) { // 以接口为方法参数
bell.ring(); // 调用ring方法。
}
}
3、成员内部类:定义在外部类的成员位置
1、作用域:和外部成员其他类一样,为整个外部类体。
2、外部类访问内部类:创建对象再去访问。
class Outer {
private int n1 = 10;
public String name = "lisi";
class Inner01 { // 地位和外部类成员一样
public void say() {
System.out.pritln("hello word");
System.out.pritln(n1); // 可以使用外部类所有成员,包括私有
}
}
public void f1() {
Inner01 inner01 = new Inner01(); // 使用内部类
inner01.say();
}
}
3、外部其他类,如何访问成员内部类:
public static void main(String[] args) {
Outer.Inner inner = Outer.new Inner();
}
class Outer {
class Inner {
}
}
4、静态内部类:放在外部类的成员位置
1、使用static修饰。
2、可以直接访问外部类的所有静态成员,不能直接访问非静态成员。
3、可以添加任意访问修饰符。
class Outer {
private int n1 = 10;
public String name = "loso";
static class Inner {
public void say() {
System.out.println(name);
}
}
public void m1() {
Inner inner = new Inner();
inner.say();
}
}
4、外部其他类使用静态内部类:
class Other {
Outer.Inner inner = new Outer.Inner(); // 因为是静态内部类,可以通过外部类名直接访问(但是要满足访问权限)
}
class Outer {
static class Inner {]
}