一: 什么是内部类(Java 内部类,成员类,局部类,匿名类等):
根据内部类的位置不同,可将内部类分为
1. 成员内部类
2. 局部内部类
class A{
//成员内部类
class B{
}
public void show1() {
//局部内部类
class C{
}
}
}
1.1成员内部类
成员内部类的访问格式:
外部类名.内部类名 对象名=外部类对象.内部类对象
class A{
//成员内部类
class B{
public void show() {
System.out.println("hello");
}
}
}
public class niming {
public static void main(String[]args) {
//创建对象访问对应内部类函数成员
A.B b=new A().new B();
b.show();
}
}
虽然内部类可以以上述方式访问,但实际开发中,常将内部设为私有成员,以保护数据的安全,不让外部直接使用
1.1.1将内部类设为私有成员
class C{
//成员内部类,设为私有
private class B{
public void show() {
System.out.println("你好");
}
}
public void show1() {
B b=new B();
b.show();
}
}
public class niming {
public static void main(String[]args) {
//创建对象访问对应内部类函数成员
C c=new C();
c.show1();
}
}
1.1.2 将内部类设为静态类
特定:静态内部类访问外部类成员时,只能访问外部类的静态成员
class C{
//静态成员变量
private static int num=20;
//静态成员方法
public static void show2() {
System.out.println("hello world");
}
//成员内部类,设为私有静态
private static class B{
public void show() {
System.out.println(num);
show2();
}
}
public void show1() {
B b=new B();
b.show();
}
}
public class niming {
public static void main(String[]args) {
//创建对象访问对应内部类函数成员
C c=new C();
c.show1();
}
}
Private 保证了数据的安全
Static 让数据访问更方便
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
//三个变量对应的变量名相同
//访问的是本方法中的num变量,满足就近原则
System.out.println(num);
//通过this变量,访问方法外,本类中的变量,this代表Inner类对象
System.out.println(this.num);
//通过外部类名加this,返回外部类对象,访问对象外部类的num
System.out.println(Outer.this.num);
//通过创建对象的方式访问外部成员变量不推荐
//System.out.println(new Outer().num);
}
}
}
public class niming {
public static void main(String[]args) {
Outer.Inner inner=new Outer().new Inner();
inner.show();
}
}
1.2 局部内部类
可以直接访问外部类的成员
内部类访问的外部变量必须定义为 final 或 static 类型
public class Outer{
public void inner(){
final int num = 5;
class InnerClass{
private int num = 4;
public void testNum(){
System.out.println(this.num);
}
}
}
}
首先,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。
二: 匿名内部类
2.1 概述
- 概念:即内部类的简化写法
- 前提:存在一个类(可以是具体类也可以是抽象类)或接口
- 格式:new 类名或接口名{重写的方法}
- 本质:创建的是继承了类或实现了接口的子类匿名对象。
2.2 Java语言规范上是这么描述匿名类的,匿名类的声明:
- 匿名类的声明是由java编译器自动派生自一个类实例创建表达式。
- 匿名类永远不能是抽象的。
- 匿名类总是隐式的final。
- 匿名类总是一个内部类;并且不能是static的。
2.3 匿名构造函数:
匿名类不能有显式声明的构造函数。相反的,Java编译器必须为这个匿名类自动提供一个匿名构造函数。
实际使用中我们只需注意这几点:
2.4 编译时的命名规则:
- 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
- 匿名内部类中是不能定义构造函数的。
- 匿名内部类中不能存在任何的静态成员变量和静态方法。
- 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
内部类的class文件命名是:主类+$+内部类名
匿名类的class文件命名是:主类+$+(1,2,3….)
2.5匿名类的语法
Runnable hello = new Runnable() {
public void run() {
System.out.println("hello");
}
};
2.6 一个匿名类由以下几个部分组成:
new操作符
Runnable:接口名称。这里还可以填写抽象类、普通类的名称。
():这个括号表示构造函数的参数列表。由于Runnable是一个接口,没有构造函数,所以这里填一个空的括号表示没有参数。
{…}:大括号中间的代码表示这个类内部的一些结构。在这里可以定义变量名称、方法。跟普通的类一样。
2.7 访问权限
- 访问外层Class里面的字段。
- 不能访问外层方法中的本地变量。除非变量是final。
- 如果内部类的名称和外面能访问的名称相同,则会把名称覆盖掉。
- 不能定义静态初始化代码块
- 不能在匿名类里面定义接口
- 不能在匿名类中定义构造函数
因为匿名类没有名字,而构造函数需要把类名作为方法名才能看成构造函数。
匿名类中可以包含的东西有:
- 字段
- 方法
- 实例初始化代码
- 本地类
2.8 匿名内部类方法的访问:
//方法1:直接在new A内部类后边加点加方法,这样访问,如果方法多了,调用太麻烦
new D(){
@Override
public void ShowContext() {
System.out.println("hello");
}
}.ShowContext();
//方法2:通过创建对象来访问,多态思想
D a=new D(){
@Override
public void ShowContext() {
System.out.println("hello");
}
};
a.ShowContext();
匿名内部类在开发中的使用,一般是方法参数为接口的情况
interface D{
void ShowContext();
}
class B{
//参数为接口对象
public void show(D d) {
d.ShowContext();
}
}
public class niming {
public static void main(String[]args) {
B b=new B();
//调用时使用匿名类创建匿名对象
b.show(new D(){
@Override
public void ShowContext() {
System.out.println("hello");
}
});
}
}
三:问题
3.1 非静态匿名内部类引起的内存泄漏
非静态内部类的对象会隐式强引用其外围对象,所以在内部类未释放时,外围对象也不会被释放,从而造成内存泄漏。
参考文章:https://blog.csdn.net/yin__ren/article/details/79177597