java内部类与外部类
概念:在一个类的内部再定义一个完整的类。
特点:
- 编译之后可生成独立的字节码文件。
- 内部类可直接访问外部类的私有成员,而不破坏封装。
- 可为外部类提供必要的内部功能组件。
一、内部类的作用
提供良好的封装环境
二、内部类分类
成员内部类、局部内部类、匿名内部类
一、成员内部类
在类的内部定义一个类,与成员方法和成员变量同级,成员内部类又分为静态内部类和非静态内部类
1、非静态内部类
1)非静态内部类相当于是特殊的成员变量,
2)非静态内部类可以在内部类中直接调用外部类的所有权限的成员变量和成员方法,外部类不能直接调用内部类的成员变量和成员方法
3)与成员变量和局部变量的优先级一致,当外部类与内部类存在同名属性时,优先访问内部类属性,要访问外部类的属性,可以这样写:
外部类名.this.属性名 即 Outer.this.name 或外部类对象.属性名 即new outer().name
4)内部类属于外部类对象,所以必须与外部类对象绑定在一起(理解为对象调用属性的方式,其中内部类对象就是外部类的属性):
Inclass in = new Outclass().new Inclass();
new outclass()可以写成引用方式,如:
Outclass outer = new Outclass();
Inclass in = outer.new Inclass();
5)如果在外部类调用外部类方法,则可以不用new:
Inclass in = new Outclass().out();
6)非静态内部类中不能含有静态方法、变量、代码块,但是可以包含静态常量
7)在外部类的静态方法、代码块不能访问非静态内部类,不能使用非静态内部类定义变量、创建实例
8)其他外部类访问内部类的三种方式:
第一种方式:Outclass outer = new Outclass(); Inclass in = outer.new Inclass();
第二种方式:Inclass in = new Outclass().new Inclass();
第三种方式:在内部类所属外部类中定义一个方法,在该方法中创建内部类对象
Inclass in = new Outclass().getInclassInstance();
2、静态内部类
1)相当于外部类,即使用方法跟外部类一致
不依赖外部类对象,可以直接创建或通过类名访问,可声明静态成员
2)属于类的,不属于具体对象
3)外部类属性与内部类属性的用法
public class Test {
public static void main(String[] args) {
new Outer.Inner().P();//第一种调用方式
System.out.println("----------------");
new Outer().show();//第二种调用方式
}
}
class Outer{
int name = 1;
//Test test = new Test();//包含静态内部类时,不能在外部类体非方法中实例化外部类对象
static class Inner{
int name = 5;
static int name1 = 5;
public static void P(){//静态和非静态方法访问属性的方式基本相同
System.out.println(new Outer().name);//访问外部类属性
System.out.println(new Inner().name);//访问内部类非静态属性
System.out.println(name1);//访问内部类静态属性
}
}
public void show(){
Inner.P();
}
}
二、局部内部类
在方法中定义内部类,只能在该方法中使用,不能添加任何访问修饰符
1)局部内部类不能定义为静态类,不能存在静态方法、属性,静态常量除外
2) 要想使用局部内部类,必须在外部类方法中实例化内部类对象,因为外部类对象只能调用外部类方法,无法在方法外面调用方法内的变量,而局部内部类的等级与局部变量相同,所以外部类对象也无法直接调用局部内部类,而当我们调用方法时,会被方法忽略,因为方法被调用时局部内部类没有创建对象,所以要想局部内部类有效,则必须在方法体内实例化内部类,并使用局部内部类对象调用内部类的方法
3)外部的其他类不能访问局部内部类(因为局部内部类相当于是一个局部变量)
4) 局部内部类不能添加访问修饰符
public class Test {
int name = 1;
static int i = 2;
public void outer() {
String n = "北方";
/**
* 注意:这里的局部变量正常情况下不是常量,即系统没有默认添加final修饰
* 当局部变量被局部内部类调用时,系统会自动添加final修饰该变量
* 即内部类没有调用的局部变量还是可以做出二次赋值的,但是如果该变量被
* 局部内部类调用或者可能会被调用的局部变量都不应该进行二次赋值,因为
* 被调用之后会变成常量,赋值的时候不会报错,但是当局部内部类调用时将会
* 报错
*/
class Inner {
int name = 5;
int name1 = 5;
public void P() {
System.out.println(i);//访问外部类静态属性
System.out.println(new Inner().name);//访问内部类非静态属性
System.out.println(name1);//访问内部类静态属性
/**
* 注意:在jdk1.7时,要在外部类局部变量前加上final修饰
* 在jdk1.8时,默认在外部类局部变量前加上final,所以可以直接
* 访问
* 原因:局部变量在方法结束时销毁,但是对象还在调用该变量,这样会出
* 错,所以必需变为常量
*/
System.out.println(n);//访问外部类的局部变量
}
}
/**
* 要想使用局部内部类,必须在内部类所在的外部类方法中实例化内部类对象,再调用内部类方法
*/
Inner inner = new Inner();
inner.P();
}
}
三、匿名内部类(重要)
定义类、实现类、创建对象的语法合并,只能创建一个对象,用于只使用一次的类,比如监听器,因为构造器名必须与类名一致,而匿名内部类没有类名,所以匿名内部类不能有构造方法
- 没有类名的局部内部类(一切特征都与局部内部类相同)注意:这里所谓的没有类名是指程序员无法给他指定类名,其实在jvm中还是会给他定义类名的,格式为
声明所在的外部类$数字
,即在哪个类创建匿名内部类,类名就是该类$
数字。
public class Test {
public static void main(String[] args) {
A a = new A();
a.aaa();
new Test().bbb();
System.out.println(a);
}
public void bbb() {
/**
* jvm会自动为匿名内部类命名,规则是 声明匿名内部类的外部类名$数字
*/
new A() { // 类名Test$1
public void bb() {
System.out.println(getClass());
}
}.bb();
new A() {// 类名Test$2
public void bb() {
System.out.println(getClass());
}
}.bb();
}
}
class A {
public void aaa() {
new A() {// 类名A$1
public void i() {
System.out.println(getClass());
}
}.i();
}
}
- 必须继承一个父类或者实现一个接口。
- 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象。匿名内部类是类也是对象,即它本质上是一个类,同时也代表着它是一个子类对象,即匿名内部类是继承该类或实现该接口的子类匿名对象
public class Test {
public static void main(String[] args) {
new Test().bbb();
}
public void bbb() {
/**
* 注意有对象名和没有对象名的区别:
* 1. 不使用对象名时,该匿名对象只能使用一次,且可以调用子类/实现类自身特有的方法
* 2. 有对象名时,该对象可以多次使用,但只可以调用父类/接口/抽象类拥有的方法(向上转型)
*/
new A() { // 类名Test$1
public void bb() { System.out.println(getClass()); }
}.bb();
/**
* 相当于:
* A a = new Test$2() {
* public void bb() { System.out.println(getClass());}
* };
* class Test$2 extends A{
* ....
* }
*/
A a = new A() {// 类名Test$2
public void bb() { System.out.println(getClass());}
};
a.aaa();
//a.bb();// 不能调用子类特有的方法
}
}
class A {
public void aaa() { System.out.println("aaa"); }
}
- 优点:减少代码量。
- 缺点:可读性较差。
匿名内部类的使用场景
当做实参直接传递
public class Test {
public static void main(String[] args) {
new Test().bbb(new A() { // 将匿名内部类当做实参直接传递
public void bb() { System.out.println(getClass()); }
});
}
public void bbb(A a) {
System.out.println(a.getClass());
}
}
class A {
public void aaa() { System.out.println("aaa"); }
}