- 内部类的定义
在java中,普通类与抽象类,我们统一称之为外部类,而在外部类中定义的类,叫做内部类。
public class Father { //创建普通类Father
class Son{ //在Father类中定义Son类
public void eat() { //定义方法
System.out.println("吃东西");
}
}
}
像这样,就是一个内部类
- 内部类的分类
内部类按照其是否有类名分为有名内部类和匿名内部类,上面举例所使用的就是有名内部类。
有名内部类:使用方式类似与全局变量。
我们在用有名内部类创建对象时,必须依赖外部类的对象:
public class Father {
class Son{
public void eat() {
System.out.println("吃东西");
}
}
public static void main(String[] args) {
Father father = new Father(); //创建Faher外部类对象
Son son = father.new Son(); //引用外部类创造Son内部类对象
}
}
public class Father {
class Son{
public void eat() {
System.out.println("吃东西");
}
}
{
Son son =this.new Son(); //通过this引用外部类,this可以省略
}
}
- 匿名内部类
因为创建对象需要类名,但是匿名部类天生没有类名,所以匿名内部类定义的时候和创建对象一起定义,语法格式如下:
new 父类构造器([参数列表])|接口(){
//匿名内部类类体
}
public class Father {
public static void main(String[] args) {
Father father = new Father() { //创建外部类对象的同时创建内部类
public void eat() {
System.out.println("吃东西");
}
};
}
}
不同匿名内部类的实质:
普通类的匿名内部类实质为普通类的子类:
这段代码中创建Mammal对象时一起创建的匿名内部类可以重写Mammal中的move方法,说明其实质上是Mammal的子类。
抽象类匿名内部类实质为抽象类的子类:
@Override注解体现出重写,所以可以知道这个匿名内部类实质是抽象类的子类,因此我们需要注意,抽象类中的匿名内部类必须实现所在抽象类的全部功能,否则匿名内部类就会成为抽象类,但是匿名内部类因为没有类名无法创建对象,所以其不能有子类,因此其成为抽象类后这个匿名内部类不再有任何意义。
接口匿名内部类实质为接口的实现类。
无论是匿名内部类还是有名内部类,javac都会产生一个独立的class文件:编译之后内部类会被编译成独立的.class文件,如果该内部类为有名内部类,则有名内部类字节码文件名为外部类的类名+$+内部类类名;如果为匿名内部类,则匿名内部类字节码文件名为外部类类名+$+数字
创建匿名内部类
在指定路径中找到class文件
- 匿名内部类的特点
可以在匿名内部类中添加新的属性和方法,但是这些属性和方法不能被上转型对象所调用,只能被非上转型对象方式创建的匿名内部类对象所调用,例如:
这里匿名内部类对象是上转型对象,而由于匿名内部类实质上是这个普通类的子类,所以上转型对象不能往下访问到匿名内部类中的新增属性与方法。
只能用这种创建对象的同时调用的方式来调用新增的属性与方法。
综上,我们可以说:同一个匿名内部类对象只能调用一个新增的属性或方法,无法再调用实现的抽象方法、重写的方法或继承的方法,也无法调用继承的属性。
- 注意事项
①外部类或外部接口 public 默认的;但是直接在类中定义的内部类,可以有public ~private
public class Father {
public class son{ //直接在类中定义,可以使用访问控制符
}
public static void main(String[] args) {
class dauthter{ //非直接定义,只能使用默认的
}
}
}
②private修饰的成员变量可以在内部类中使用
这一点是由于内部类与外部类的继承关系所导致的,内部类为外部类子类,所以可以访问private所修饰的成员变量。
③局部变量不能使用访问权限
这里程序报错,是因为访问权限用于限制成员变量或方法能否在另一个类中被使用,但因为局部变量已经限定,所以局部变量不能使用访问权限。
④如果在内部类中使用局部变量的值,则:JDK8.0及+可以不适用final修饰,8.0以前必须使用fianl
public void eat(final String name) { //final修饰
class Baby{
{
System.out.println(name);
}
}
}
因为内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。而如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就没了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。