7.5 内部类
7.5.1 内部类概述
1、什么是内部类?
顾名思义:一个类里面的类称为内部类。
例如:
class Outer{ //相对的,它是外部类
class Inner{ //内部类
}
}
2、为什么要用内部类?
实现高内聚低耦合的开发原则。
好处:
(1)内部类,可以被限定在外部类中使用
(2)内部类和外部类可以互相访问对方的私有的成员
例如:
设计一些数据结构,这些数据结构是单链表、双链表、二叉树等等。
class SingleLinked{ //单链表
private class Node{
Object data;//元素
Node next;//下一个元素的地址
}
}
class LinkedList{
private class Node{
Node previous;//上一个元素的地址
Object data;//元素
Node next;//下一个元素的地址
}
}
class Tree{
private class Node{
Node parent;//父结点的地址
Object data;//元素
Node left;//左结点的地址
Node right;//右结点的地址
}
}
3、内部类的形式
(1)成员内部类:声明在外部类中,在方法外面
A:静态成员内部类
B:非静态成员内部类
(2)局部内部类:声明在外部类的方法中
A:有名字的局部内部类
B:匿名的局部内部类
4、学习内部类的技巧
内部类也是类,从类的角度观察它
内部类是在外部类的里面,要么作为外部类的成员,要么作为外部类某个方法的一个局部结构,从这个角度观察它
7.5.2 静态内部类
一、静态内部类
1、语法结构
【修饰符】 class 外部类名{
【其他修饰符】 static class 静态内部类名{
}
}
2、从类的角度观察
(1)有自己的字节码文件吗?有,外部类名$静态内部类名.class
(2)是否可以继承自己的父类,实现自己的父接口呢?可以
(3)是否可以有自己的类成员
A:成员变量:静态变量、实例变量
B:成员方法:静态变量、实例方法
C:构造器:无参构造、有参构造
D:代码块:静态代码块、非静态代码块(如果需要可以加,但是一般很少)
E:内部类:可以有(一般不会写内部类的内部类)
(4)静态内部类也可以是抽象类
(5)静态内部类是可以new对象(只要不是抽象的,就可以new对象)
3、从它是外部类的成员角度
(1)修饰符:public、protected、缺省、private、static(一定有)、abstract、final
(2)在静态内部类中可以直接使用外部类的成员吗,包括私有的,静态非静态的?
可以用私有的,静态的,
但不能直接使用外部类的非静态成员。
(3)反过来,外部类是否可以直接使用静态内部类的所有成员呢,包括私有的,静态非静态的?
可以,但是要注意,
外部类使用静态内部类的非静态成员,需要先创建静态内部类的对象,然后通过“静态内部类对象.非静态成员”
外部类使用静态内部类的静态成员,需要通过“静态内部类名.静态成员”方式
(4)在外部类的外面使用静态内部类,可以吗?
可以,但是要注意权限修饰符问题。
而且静态内部类一定是依赖于外部类的,所以"外部类名.静态内部类名"
(5)在外部类的外面是否可以调用静态内部类的成员?
可以,但是要注意权限修饰符问题。
使用静态内部类的非静态成员,需要先创建静态内部类的对象,然后通过“静态内部类对象.非静态成员”
使用静态内部类的静态成员,需要通过“外部类名.静态内部类名.静态成员”方式
(6)当外部类的成员和静态内部类的成员重名了,怎么办?
和外部类的静态成员重名,通过“外部类名.静态成员”区分即可。
public class TestStaticInnerClass {
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
System.out.println(in.a);
System.out.println(Outer.Inner.b);
System.out.println("--------------");
in.method();
}
}
class Outer{
int a = 1;
static int b = 2;
static class Inner {
int a = 3;
static int b = 4;
public void method(){
System.out.println("内部类的a = " + a);
System.out.println("内部类的b = " + b);
// System.out.println("外部类的a = " + a);//不可以
System.out.println("外部类的b = " + Outer.b);
}
}
public void outMethod(){
Inner obj = new Inner();
System.out.println("内部类的a = " + obj.a);
System.out.println("内部类的b = " + Inner.b);
}
}
7.5.3 非静态内部类
一、非静态内部类
1、语法结构
【修饰符】 class 外部类名{
【其他修饰符】 class 非静态内部类名{
}
}
2、从类的角度观察
(1)有自己的字节码文件吗?有,外部类名$非静态内部类名.class
(2)是否可以继承自己的父类,实现自己的父接口呢?可以
(3)是否可以有自己的类成员
A:成员变量:实例变量
B:成员方法:实例方法
C:构造器:无参构造、有参构造
D:代码块:非静态代码块(如果需要可以加,但是一般很少)
E:内部类:可以有(一般不会写内部类的内部类)
结论:非静态内部类中不允许有静态成员。
(4)非静态内部类也可以是抽象类
(5)非静态内部类是可以new对象(只要不是抽象的,就可以new对象)
3、从它是外部类的成员角度
(1)修饰符:public、protected、缺省、private、abstract、final
(2)在非静态内部类中可以直接使用外部类的成员吗,包括私有的,静态非静态的?
可以用私有的,静态的,非静态,没有限制。
(3)反过来,外部类是否可以直接使用非静态内部类的所有非静态的成员呢,包括私有的?
可以。只不过需要创建非静态内部类的对象。
(4)在外部类的外面使用非静态内部类,可以吗?
可以,但是要注意权限修饰符问题。
A:非静态内部类一定是依赖于外部类的,所以"外部类名.非静态内部类名"
B:非静态内部类还要依赖于外部类的对象,因为它是外部类的非静态成员。
要创建非静态内部类的对象,必须先有外部类的对象。
方式一:
Outer out = new Outer();//外部类的对象
Outer.Inner in = out.new Inner();//创建非静态内部类的对象,依赖于外部类的对象
方式二:
Outer out = new Outer();//外部类的对象
Outer.Inner in2 = out.getInner();//调用外部类的某个方法,得到非静态内部类的对象
要注意外部类中得所有一个getInner()方法,里面会返回Inner对象才可以。
(5)在外部类的外面是否可以调用非静态内部类的非静态成员?
可以,但是要注意权限修饰符问题。
先创建外部类对象,再创建非静态内部类的对象,再通过“非静态内部类的对象.非静态成员”
(6)当外部类的成员和非静态内部类的成员重名了,怎么办?
A:和外部类的静态成员重名,通过“外部类名.静态成员”区分即可。
B:和外部类的非静态成员重名,通过“外部类名.this.非静态成员”区分即可。
public class TestNonStaticInnerClass {
public static void main(String[] args) {
Outer out = new Outer();//外部类的对象
Outer.Inner in = out.new Inner();//创建非静态内部类的对象,依赖于外部类的对象
Outer.Inner in2 = out.getInner();//调用外部类的某个方法,得到非静态内部类的对象
in.inMethod();
in2.inMethod();
}
}
class Outer{
private int a = 1;
private static int b = 2;
class Inner{
private int a = 3;
private int b = 4;
public void inMethod(){
System.out.println("非静态内部类的非静态方法");
System.out.println("内部类的a = " + a);
System.out.println("内部类的b = " + b);
/* Outer out = new Outer();
System.out.println("外部类类的a = " + out.a);//可以*/
System.out.println("外部类类的a = " + Outer.this.a);//可以更直接
System.out.println("外部类类的b = " + Outer.b);
System.out.println("内部类的a = " + this.a);
/*
这个方法中有两个当前对象:
一个是内部类自己的当前对象
另一个是外部类的当前对象
因为调用inMethod(),new了两个对象
*/
}
}
public Inner getInner(){
return new Inner();
}
}
7.5.4 静态内部类和非静态内部类的对比
静态内部类(静态成员内部类) | 非静态内部类(非静态成员内部类) | |
字节码文件名 | 外部类名$成员内部类名.class | 同左 |
继承父类实现父接口 | 可以 | 同左 |
内部类自己的成员 | 所有成员都可以 | ==不能有自己的静态成员== |
是否可以是抽象类 | 可以 | 同左 |
是否可以new对象 | 同左 | 同左 |
修饰符 | public、protected、缺省、private、abstract、final、static | ==没有static==,其他同左 |
直接使用外部类的静态成员 | 可以 | 同左 |
直接使用外部类的非静态成员 | ==不可以== | 可以 |
外部类使用内部类的静态成员 | 可以 | ==没有静态成员== |
外部类使用内部类的非静态成员 | 可以,创建内部类的对象再用 | 同左 |
依赖于外部类 | 依赖 | 同左 |
依赖于外部类的对象 | 不依赖 | 依赖 |
在外部类的外面使用内部类声明变量 | 外部类名.内部类名 变量; | 同左 |
在外部类的外面创建内部类的对象 | 外部类名.静态内部类名 变量 = new 外部类名.静态内部类名(); | (1)外部类名 变量1 = new 外部类名(); (2)外部类名.非静态内部类名 变量 = ==变量1==.new 非静态内部类名(); |
在外部类的外面使用内部类的静态成员 | 外部类名.静态内部类名.静态成员 | ==没有静态成员== |
在外部类的外面使用内部类的非静态成员 | 先创建内部类对象,再用“内部类对象.非静态成员” | 同左 |
内部类中成员和外部类的静态成员重名 | 外部类名.静态成员 | 同左 |
内部类中成员和外部类的非静态成员重名 | 不存在(因为无法直接使用外部类的非静态成员) | ==外部类名.this.非静态成员== |
如何选择? | 当使用这个内部类时,不依赖于外部类对象时,就选静态内部类 | 当使用这个内部类时,==依赖于外部类对象==时,就选非静态内部类 |
7.5.5 局部内部类
一、局部内部类(很少用,了解 * )
1、语法结构
【修饰符】 class 外部类名{
【修饰符】 返回值类型 方法名(【形参列表】){
【其他修饰符】 class 局部内部类名{
}
}
}
理论上,代码块(静态代码块或非静态代码块)、成员方法的方法体、构造器的方法体中都可以声明局部内部类,
但是通常的局部内部类是在成员方法的方法体中。
2、从类的角度观察
(1)有自己的字节码文件吗?有,外部类名$编号局部内部类名.class
为什么有编号?因为局部内部类可以重名。
(2)是否可以继承自己的父类,实现自己的父接口呢?可以
(3)是否可以有自己的类成员
A:成员变量:实例变量
B:成员方法:实例方法
C:构造器:无参构造、有参构造
D:代码块:非静态代码块(如果需要可以加,但是一般很少)
E:内部类:可以有(一般不会写内部类的内部类)
结论:局部内部类中不允许有自己静态成员。
(4)局部内部类也可以是抽象类
(5)局部内部类是可以new对象(只要不是抽象的,就可以new对象)
3、从它是外部类的局部结构角度
(1)修饰符:abstract、final
(2)在局部内部类中可以直接使用外部类的成员吗,包括私有的,静态非静态的?
可以用私有的,静态的。
但是非静态是否能访问受所在方法是否静态的限制,即 局部内部类在外部类的静态方法中,只能访问外部类的静态成员。
局部内部类在外部类的非静态方法中,可以访问外部类的所有成员,包括静态和非静态的。
(3)反过来,外部类是否可以直接使用局部内部类的所有非静态的成员呢,包括私有的?
可以。只不过需要创建局部内部类的对象,并且要注意"作用域"。
(4)在外部类的外面使用局部内部类,可以吗?不允许
(5)在外部类的外面是否可以调用局部内部类的非静态成员?
可以,但是只能通过多态引用的方式调用,所以意味着不能调用局部内部类扩展的成员,
只能调用父类声明的成员,或子类重写的成员。
(6)当外部类的成员和局部内部类的成员重名了,怎么办?
A:和外部类的静态成员重名,通过“外部类名.静态成员”区分即可。
B:和外部类的非静态成员重名,通过“外部类名.this.非静态成员”区分即可。
(7)在局部内部类中还可以使用所在方法或代码块的局部变量,但是这个局部变量必须是final的。
JDK1.8之前,必须手动加final
JDK1.8之后,自动加final
为什么要加final呢?
因为,在局部内部类中使用的这个变量,本质上已经不是外部类的这个局部变量了。
而是有一个隐形的成员变量接收了这个局部变量的值。
但是,从代码的阅读角度来说,似乎它们就是同一个,为了避免引起不必要的误解,干脆规定它必须是final的,
不能修改的,那么就算大家认为它们是同一个也没有关系。
public class TestLocalInner {
// private static Father f;
public static void main(String[] args) {
// Inner in = new Inner();//超过作用域了,不能直接使用Inner类
// outMethod();
// f.inMethod();
Father subClassOfFather = getSubClassOfFather();//多态引用
/*
通过外部类的一个方法,返回内部类的对象,
接收这个内部类的对象,用内部类的父类或父接口的变量接收。
*/
subClassOfFather.inMethod();
}
public static Father getSubClassOfFather(){
final int num = 10;//局部变量
class Inner extends Father{//局部内部类
private int inA;
public void inMethod(){
// System.out.println("outA = " + outA);//因为outMethod()是静态的,不能使用外部类的非静态outA
System.out.println("outB = " + outB);
System.out.println("num = " + num);
}
}
return new Inner();
}
private int outA;
private static int outB;
public static void outMethod(){
int a = 1;//局部变量
class Inner extends Father{//局部内部类
private int inA;
public void inMethod(){
// System.out.println("outA = " + outA);//因为outMethod()是静态的,不能使用外部类的非静态outA
System.out.println("outB = " + outB);
}
}
Inner obj = new Inner();
// f = obj;
System.out.println("inA = " + obj.inA);
obj.inMethod();
}
public void outTest(){
class Inner{
public void inMethod(){
System.out.println("outA = " + outA);
System.out.println("outB = " + outB);
}
}
}
}
abstract class Father{
public abstract void inMethod();
}
public class Outer {
public static void main(String[] args) {
int a = 1;
class Inner{
/*private int a;
public Inner(int a){
this.a = a;
}*/
public void method(){
System.out.println(a);
}
}
}
}
7.5.6 成员内部类和局部内部类的对比
静态内部类(静态成员内部类) | 非静态内部类(非静态成员内部类) | 局部内部类 | |
字节码文件名 | 外部类名$成员内部类名.class | 同左 | 外部类名$==编号==成员内部类名.class |
继承父类实现父接口 | 可以 | 同左 | 同左 |
内部类自己的成员 | 所有成员都可以 | ==不能有自己的静态成员== | ==不能有自己的静态成员== |
是否可以是抽象类 | 可以 | 同左 | 同左 |
是否可以new对象 | 同左 | 同左 | 同左 |
修饰符 | public、protected、缺省、private、abstract、final、static | ==没有static==,其他同左 | 只能abstract或final,或没有 |
直接使用外部类的静态成员 | 可以 | 同左 | 同左 |
直接使用外部类的非静态成员 | ==不可以== | 可以 | 要看所在方法是否是静态的,如果是静态方法就不可以,如果是非静态方法,就可以。 |
外部类使用内部类的静态成员 | 可以 | ==没有静态成员== | ==没有静态成员== |
外部类使用内部类的非静态成员 | 可以,创建内部类的对象再用 | 同左 | 同左,并要注意==作用域== |
依赖于外部类 | 依赖 | 同左 | 同左 |
依赖于外部类的对象 | 不依赖 | 依赖 | 要看所在方法是否是静态的,如果是静态方法就不依赖,如果是非静态方法,就依赖。 |
在外部类的外面使用内部类声明变量 | 外部类名.内部类名 变量; | 同左 | ==不可以== |
在外部类的外面创建内部类的对象 | 外部类名.静态内部类名 变量 = new 外部类名.静态内部类名(); | (1)外部类名 变量1 = new 外部类名(); (2)外部类名.非静态内部类名 变量 = ==变量1==.new 非静态内部类名(); | 只能通过内部类的父类或父接口的变量,接收内部类的对象,局部内部类的对象只能在所在方法中创建。 |
在外部类的外面使用内部类的静态成员 | 外部类名.静态内部类名.静态成员 | 没有静态成员==== | ==没有静态成员== |
在外部类的外面使用内部类的非静态成员 | 先创建内部类对象,再用“内部类对象.非静态成员” | 同左 | 只能多态引用的形式访问,访问继承的或重写的成员,不能是自己扩展的成员。 |
内部类中成员和外部类的静态成员重名 | 外部类名.静态成员 | 同左 | 同左 |
内部类中成员和外部类的非静态成员重名 | 不存在(因为无法直接使用外部类的非静态成员) | ==外部类名.this.非静态成员== | 同中 |
如何选择? | 当使用这个内部类时,不依赖于外部类对象时,就选静态内部类 | 当使用这个内部类时,==依赖于外部类对象==时,就选非静态内部类 | 当这个内部类仅限于当前方法有用,就可以用局部内部类(很少很少用) |
使用外部类的局部变量 | 不可以 | 不可以 | 只能用当前方法final修饰的局部变量 |
7.5.7 匿名内部类
1、首先,匿名内部类也是局部内部类,只是它没有名字。
2、语法格式:
(1)new 父类(){
}
(2)new 父类(实参列表){
}
(3)new 父接口(){
}
3、特殊
(1)这个内部类没有名字,所以必须在声明的同时,就创建它唯一的对象。
(2)这里new对象,指定了父类名或父接口名,指明了它是从哪个类别派生出来的。
匿名内部类又称为匿名子类或匿名实现类。
(3)因为匿名,所以这个类无法写构造器,只有默认的无参构造。
(4)因为没有class关键字等声明形式,所以匿名内部类没有任何修饰符。
4、从类的角度
(1)有自己的字节码文件吗?有,外部类名$编号.class
(2)是否可以继承自己的父类,实现自己的父接口呢?可以
其他内部类可以同时继承父类又实现接口,
但是匿名内部类只能指定父类或父接口。
new 父类(【实参列表】){} 指定父类
new 父接口(){}指定父接口
(3)是否可以有自己的类成员
A:成员变量:实例变量
B:成员方法:实例方法
C:构造器:默认的无参构造(无法手动编写构造器)
D:代码块:非静态代码块(如果需要可以加,但是一般很少)
E:内部类:可以有(一般不会写内部类的内部类)
结论:匿名内部类中不允许有静态成员,也不能手动编写构造器。
但是,一般很多在匿名内部类中写很多成员,而且一般也不扩展自己的成员,
通常都是重写父类或父接口的方法。
(4)匿名内部类不可以是抽象类,不能加修饰符abstract
(5)匿名内部类是可以new对象:一定会有对象,而且在声明类的同时就创建了唯一对象了
5、如何使用匿名内部类?
(1)匿名子类的匿名对象.方法
new 父类(【实参列表】){....}.方法();
(2)多态引用
父类 变量 = new 父类(【实参列表】){....};
变量.方法(); //只能调用重写的方法
父接口 变量 = new 父接口(){....};
变量.方法(); //只能调用重写的方法
(3)匿名子类或匿名实现类的对象作为另一个方法调用的实参
6、语法格式说明:
(1)new 父类(){
}
表示匿名子类的构造器首行是 super();,调用父类的无参构造。
(2)new 父类(实参列表){
}
表示匿名子类的构造器首行是 super(实参列表);,调用父类的有参构造。
(3)new 父接口(){
}
表示匿名子类的构造器首行是 super();,调用父类的无参构造。它的父类是默认父类Object类。
public class TestNoNameInnerClass {
public static void main(String[] args) {
new Object();//创建的是Object类的对象
new Object(){
};//创建的是Object的匿名子类的对象
System.out.println("------------下面演示使用---------------");
new Object(){
public void method(){
System.out.println("匿名内部类扩展的自己的方法");
}
}.method();
//Object父类的匿名子类的匿名对象,调用method方法
Father f = new Father() {//创建Father类的匿名子类的对象
@Override
public void method() {
System.out.println("重写父类的抽象方法");
}
};//多态引用
f.method();
Flyable fObj = new Flyable() {//创建的是Flyable接口的匿名实现类的对象
@Override
public void fly() {
System.out.println("重写接口的抽象方法");
}
};
fObj.fly();
test(new Father() {
@Override
public void method() {
System.out.println("另一个匿名子类的对象重写method方法");
}
});
new Mother("虎妞"){
@Override
public void method() {
System.out.println("重写方法" + info);
}
}.method();
}
public static void test(Father f){
f.method();
}
}
abstract class Father{
public abstract void method();
}
interface Flyable{
void fly();
}
abstract class Mother{
protected String info;
public Mother(String info) {
this.info = info;
}
public abstract void method();
}
标签:部类,Java,内部,静态,成员,十二,笔记,new,out
From: https://blog.51cto.com/u_16213911/7119220