内部类
内部类不是在一个java源文件中编写俩个平行的类,而是在一个类的内部再定义另外一个类。
例如:
Person.java文件中
//虽然这个俩个类在一个文件中,但是它们是平行的关系
//所以这种情况不是内部类
public class Person{
}
class PersonTest{
}
例如:
Person.java文件中
public class Person{
//这个就是一个内部类
//类A的是定义在Person类中的
public class A{
}
}
注意,内部类是类和类之间的嵌套关系
我们可以把定义在外面的类叫做外部类,嵌套定义在里面的类叫做内部类。
java中的内部类,可以分为四种:成员内部类,静态内部类,局部内部类,匿名内部类
通过以下几个方法,对内部类进行学习了解:
在语法上,该内部类如何编写
在该内部中,都可以定义哪些属性和方法
内部类和外部类之间,如何相互方法对方的属性和方法
在外部类中,以及在其他类的代码中,该如何创建这个内部类对象
最后还需要从意义上,了解为什么要编写内部类
1 成员内部类
//成员内部类例子
//MemberOutterClass是当前外部类
public class MemberOutterClass{
/* 成员内部类 声明开始 */
public class MemberInnerClass{
}
/* 成员内部类 声明结束 */
}
定义属性和方法、构造器
例如:
public class MemberOutterClass{
/* 成员内部类 声明开始 */
public class MemberInnerClass{
private String name;
private int age;
public void run(){}
}
/* 成员内部类 声明结束 */
}
注意,成员内部类中,【不能】编写静态的属性和方法
注意,成员内部类中,可以编写构造器,如果有需要的话,如果不编写的话,那么默认使用无参的构造器。
成员内部类和外部类的相互访问
例如:
public class MemberOutterClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void test(){
//在外部类中,任何访问成员内部类中的属性和方法
//MemberInnerClass mic = this.new MemberInnerClass();
//在外部类中,可以把 this. 省去
//这个意思就是成员内部类对象的创建,是需要依托于外部类对象的
//就像一个类中的成员属性、成员方法,需要依托于这个类的对象才能访问
MemberInnerClass mic = new MemberInnerClass();
System.out.println(mic.name);
System.out.println(mic.age);
mic.run();
}
/* 成员内部类 声明开始 */
public class MemberInnerClass{
private String name;
private int age;
public void run(){}
public void test(String name){
//这个方法的输参数name
System.out.println(name);
//访问当前内部类中name属性
System.out.println(this.name);
System.out.println(MemberInnerClass.this.name);
//访问外部类中的name属性(非静态)
System.out.println(MemberOutterClass.this.name);
//访问外部类中的age属性(静态)
System.out.println(MemberOutterClass.age);
//访问外部类中的run方法(非静态)
MemberOutterClass.this.run();
//访问外部类中的go方法(静态)
MemberOutterClass.go();
//访问内部类中自己的run方法
MemberInnerClass.this.run();
}
}
/* 成员内部类 声明结束 */
}
注意,在内部类中,可以使用 类名.this 的形式,当前使用的this指的是哪一个类中的this
注意,在外部类中,要访问成员内部类中的属性和方法,需要先创建这个成员内部类的对象
注意,成员内部类对象,是要依托于外部类对象,也就是一定先创建了外部类对象,然后才能创建这个成员内部类对象。
在其他类中,怎么使用这个成员内部类创建对象
注意,首先要先确认这个内部类的访问控制修饰符使用的是哪一个,如果是private修饰的话,那么在其他类中,就不可能访问到这个成员内部类
注意,在其他类中使用这个内部类的时候,一定要先进行import导入
例如:
import com.briup.day26.MemberOutterClass.MemberInnerClass;
public class MemberOutterClass{
/* 成员内部类 声明开始 */
public class MemberInnerClass{
private String name;
private int age;
public void run(){}
}
/* 成员内部类 声明结束 */
}
class MemberOutterClassTest{
public static void main(String[] args){
//MemberOutterClass moc = new MemberOutterClass();
//MemberInnerClass mic = moc.new MemberInnerClass();
MemberInnerClass mic = new MemberOutterClass().new MemberInnerClass();
mic.run();
}
}
2 静态内部类
格式:
//静态内部类的例子
public class StaticOutterClass{
/* 静态内部类 声明开始 */
public static class StaticInnerClass{
}
/* 静态内部类 声明结束 */
}
定义属性和方法、构造器:
public class StaticOutterClass{
/* 静态内部类 声明开始 */
public static class StaticInnerClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
}
/* 静态内部类 声明结束 */
}
注意,静态内部类中,【可以】编写静态的属性和方法,另外在四种内部类中,只有静态内部类可以编写静态属性和方法
注意,静态内部类中,可以编写构造器,如果有需要的话,如果不编写的话,那么默认使用无参的构造器。
内部类和外部类的相互访问:
例如:
public class StaticOutterClass{
private String name;
private static int age;
private void run(){}
private static void go(){}
public void test(){
//在外部类中,访问静态内部类中的静态属性和方法
System.out.println(StaticInnerClass.age);
StaticInnerClass.go();
//在外部类中,访问静态内部类中的非静态属性和方法
//这时候需要先创建静态内部类对象,使用对象来访问
StaticInnerClass sic = new StaticInnerClass();
System.out.println(sic.name);
sic.run();
}
/* 静态内部类 声明开始 */
public static class StaticInnerClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void test(String name){
//这name是参数
System.out.println(name);
//静态内部类访问自己的属性和方法
//静态 和 非静态的都可以访问
System.out.println(this.name);
System.out.println(StaticInnerClass.this.name);
System.out.println(StaticInnerClass.age);
StaticInnerClass.this.run();
StaticInnerClass.go();
//静态内部类中访问外部类中的静态属性和静态方法
//注意,在静态内部类中访问不了外部类中的非静态属性和方法
//System.out.println(StaticOutterClass.this.name);
System.out.println(StaticOutterClass.age);
StaticOutterClass.go();
}
}
/* 静态内部类 声明结束 */
}
注意,在静态内部类中访问不了外部类中的非静态属性和方法
注意,在外部类中,如果要访问静态内部类中的静态属性和方法,那么可以直接使用 内部类的名字来访问,如果要访问静态内部类中的非静态属性和方法,那么这个时候就需要先创建内部类对象,然后使用对象再访问。
在其他类中,怎么使用这个静态内部类创建对象
注意,要先确认这个内部类的 访问控制修饰符 使用的是哪一个,如果是private修饰的话,那么在其他类中,就不可能访问到这个成员内部类
注意,在其他类中使用这个内部类的时候,一定要先进行import导入
注意,静态内部类和成员内部类不同,静态内部类由于是静态的,它可以独立存在,并不会依赖于外部类的对象。而成员内部类就必须依赖于外部类对象,也就是必须先要创建出外部类对象,才能接下来创建成员内部类对象。
例如:
import com.briup.day26.StaticOutterClass.StaticInnerClass;
public class StaticOutterClass{
/* 静态内部类 声明开始 */
public static class StaticInnerClass{
public void run(){}
}
/* 静态内部类 声明结束 */
}
class StaticOutterClassTest{
public static void main(String[] args){
//直接使用静态内部类来创建对象,不需要依赖外部类
//但是要记得先import导入
StaticInnerClass sic = new StaticInnerClass();
sic.run();
}
}
3 局部内部类
注意,这个是四种内部类中,使用的最少最少的一种内部类形式
格式:
//局部内部类的例子
public class LocalOutterClass{
public void test(){
/* 局部内部类 声明开始 */
class LocalInnerClass{
}
/* 局部内部类 声明结束 */
}
}
注意,局部内部类是定义在方法中的一种内部类,这种情况下,这个内部类的作用范围就比较小了,只能在方法中起作用,出了这个方法代码块,局部内部类就没有了。
定义属性和方法、构造器:
//局部内部类的例子
public class LocalOutterClass{
public void test(){
/* 局部内部类 声明开始 */
class LocalInnerClass{
private String name;
private int age;
public void run(){}
}
/* 局部内部类 声明结束 */
}
}
注意,局部内部类中,【不能】编写静态的属性和方法
注意,局部内部类中,如果有需要的话,可以编写构造器,如果不编写的话,那么默认使用无参的构造器。
局部内部类和外部类的相互访问:
例如:
public class LocalOutterClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void say(final String myName){
final int a = 1;
/* 局部内部类 声明开始 */
class LocalInnerClass{
private String name;
private int age;
public void run(){}
public void test(String name){
//访问当前最近的name变量,也就是这个参数
System.out.println(name);
//访问say方法中的myName属性
//也可以访问这个方法中的局部变量
//但是要求这个局部变量必须是final修饰的
//JDK1.8的时候,这个局部变量不是final修饰的也可以被局部内部类访问,但是访问之后,这个变量就自动变为final的了。
System.out.println(myName);
System.out.println(a);
//访问LocalInnerClass类中的属性name
System.out.println(this.name);
System.out.println(LocalInnerClass.this.name);
//访问LocalOutterClass类中的属性和方法(静态和非静态)
System.out.println(LocalOutterClass.this.name);
System.out.println(LocalOutterClass.age);
LocalOutterClass.this.run();
LocalOutterClass.go();
}
}
/* 局部内部类 声明结束 */
//由于局部内部类在方法中,所以也只能在这个方法中使用
LocalInnerClass lic = new LocalInnerClass();
System.out.println(lic.name);
System.out.println(lic.age);
lic.run();
}
}
在其他类中,怎么使用这个内部类创建对象
出了这个局部内部类所在的方法之后,其他任何地方都使用不到这个局部内部类
4 匿名内部类
注意,匿名内部类是将来我们在程序中,使用最多的一种内部类形式。
注意,匿名内部类可以定义在方法中,也可以在类中属性赋值的时候进行使用。但是绝大多数都会是在方法中进行定义和使用。
注意,匿名内部类由于没有名字,所以在定义的时候形式上也会比较特殊一些。
匿名内部类必须依附在一个类或者接口上来进行定义,如果是依附在一个类上,那么这个匿名内部类就默认是这个类的子类。如果是依附在一个接口上,那么这个匿名内部类就默认是这个接口的实现类。
注意,一个普通的类,都是先声明,然后再使用这个来创建对象,但是匿名内部类,就必须在声明的同时就创建出这个类的对象,并且只能使用这一次。因为它没有名字。
格式:
public class AnonymousOutterClass{
//在给类中属性private Person person; 赋值的时候
//=号右边使用了匿名内部类对象,来给=号左边的引用person进行赋值
private Person person = new Person(){
public void run(){
}
};
public void test(){
//虽然抽象类不能直接new对象,但是我们可以依附在这个抽象类上,声明出一个匿名内部类,并且同时就创建这个匿名内部类的对象。
//注意,这个里的这个大括号,就是这个匿名内部类的代码实现
//这个匿名内部类,就相当于继承了Person这个父类型
//父类型的引用,指向子类对象,只不过这次的这个子类对象,是一个匿名内部类对象。
Person p = new Person(){
public void run(){
System.out.println("这里就是匿名内部类的实现");
}
};
//虽然接口不能直接new对象,但是我们可以依附在这个接口上,声明出一个匿名内部类,并且同时就创建这个匿名内部类的对象。
//这个匿名内部类,默认就是对这个接口的实现
Action a = new Action(){
public void go(){
System.out.println("这里就是匿名内部类的实现");
}
};
}
}
abstract class Person{
public abstract void run();
}
interface Action{
public void go();
}
定义属性和方法、构造器
在这个点上,匿名内部类和其他的内部类不太一样。其他的内部类都可以定义自己的属性、方法、构造器,然后将来将来需要的时候可以使用。
匿名内部类,虽然可以定义属性和方法,但是定义之后无法调用到。
匿名内部类,我们是没有办法编写构造器的,因为这个类没有名字,但是编译之后会自动生成构造器的,通过javap就可以看到这个构造器的声明。
总结:在匿名内部类中,一般情况,我们不会编写单独的属性、方法(如果有需要的话,可以编写),因为在外面不能能直接调用,我们也编写不了构造器,因为没有名字。我们在匿名内部类中,做的最多的事情,就是【重写】父类中的方法,或者【实现】接口中的抽象方法。
例如:
Action a = new Action(){
private String name;
public void run(){
}
//go方法是实现接口Action中的抽象方法
public void go(){
this.name = "tom";
this.run();
System.out.println("这里就是匿名内部类的实现");
}
};
//编译报错
a.run();
因为run方法是匿名内部类中独有的方法,变量a是Action类型的,Action中没有定义run方法,所以使用变量a无法调用到run方法
把变量a进行类型转换,转为这个匿名内部类的类型就可以了,因为这个run方法就是这个匿名内部类中独有的。
但是由于匿名内部类没有名字,所以我们也无法把变量a转为这个类型。那么也就是我们无法直接调用到这个独有的run方法。
但是可以【间接】的调用到,例如可以在go方法中调用run方法,然后再使用变量a俩调用go方法,最后也可以让run方法执行。
总结:在你们内部类中,定义直接的独有的属性和方法,一般情况意义不大,除非是想在这匿名内部类中自己单独使用。因为外部是没有办法【直接】调用到的。
匿名内部类和外部类的相互访问:
匿名内部类在方法中定义的时候,其实就是一个没有名字的局部内部类,所以这时候它和外部类相互访问的情况和局部内部类是一样的。
例如:
public class AnonymousOutterClass{
private String name;
private static int age;
public void test(){
final int num = 1;
Person p = new Person(){
public void run(){
System.out.println(num);
System.out.println(AnonymousOutterClass.this.name);
System.out.println(AnonymousOutterClass.age);
}
};
p.run();
}
}
在其他类中,怎么使用这个内部类创建对象
匿名内部类大多定义在方法中,并且没有名字,所以其他地方都无法使用这个匿名内部类。但是我们可以使用return语句把这个匿名内部类的对象,返回出去,让别人使用。
例如:
public Person test(){
final int num = 1;
Person p = new Person(){
public void run(){
System.out.println(num);
System.out.println(AnonymousOutterClass.this.name);
System.out.println(AnonymousOutterClass.age);
}
};
return p;
}
- 不同内部类的选择
例如现在已经确定了要使用内部类,那么接下来该如何选择?
1.考虑这个内部类,如果需要反复的进行多次使用
1.1 在这个内部类中,如果需要定义静态的属性和方法
选择使用静态内部类
1.2 在这个内部类中,如果需要访问外部类的非静态属性和方法
选择使用成员内部类
2.考虑这个内部类,如果只需要使用一次
选择使用匿名内部类
注意,基本没有什么情况下,我们会选择局部内部类,这种内部类几乎不用。
6. 内部类的意义(为什么要使用内部类)
例如现在有一个类A,在类A中需要完成一些工作,在这个过程中,需要另一个类B进行辅助,这个类B是专门针对类A进行设计编写的,在其他类中根本不会用到这个类B,这时候,考虑到类A和类B之间的这种紧密的联系,那么我们就可以把类B以内部类的形式,定义在类A中。
例如现在有一个类A,还有一个类B,这俩个类需要相互访问对方的属性或者方法,共同合作来完成一些功能。基于这种特点,为了能让类A和类B之间更加方便的进行相互访问,那么我们也可以考虑以内部类的形式,把类B定义在类A之中
注意,很多使用内部类来完成的功能/效果,其实不使用内部类,也就是用普通的类,也能完成,但是可能这样的话,俩个类之间的相互访问没内部类的形式容易,另外没不能突出这俩个类之间的紧密的关系。还有就是也行分开后的这个类,在其他地方根本就不会被使用,因为这个类作为一个辅助的类,只是辅助我们的主类来完成一些操作的,在其他代码中根本用不上。