Java内部类和外部类
在Java语言中,可以把一个类定义到另外一个类的内部,在类里面的这个类就叫作内部类,外面的类叫作外部类。在这种情况下,这个内部类可以被看成外部类的一个成员(与类的属性和方法类似)。还有一种类被称为顶层(Top-level)类,指的是类定义代码不嵌套在其他类定义中的类。
内部类
内部类主要有四种类型:
- 静态内部类(Static Inner Class)
- 成员内部类(Member Inner class)
- 局部内部类(Local Inner Class)
- 匿名内部类(Anonymous Inner Class)
静态内部类
静态内部类是指被声明为static的内部类,它可以不依赖于外部类实例而被实例化,而通常的内部类需要在外部类实例化后才能实例化。静态内部类不能与外部类有相同的名字,不能访问外部类的普通成员变量,只能访问外部类中的静态成员和静态方法(包括私有类型)。
一个静态内部类,如果去掉“ static”关键字,就成为成员内部类。成员内部类为非静态内部类,它可以自由地引用外部类的属性和方法,无论这些属性和方法是静态的还是非静态的。但是它与一个实例绑定在了一起,不可以定义静态的属性和方法。只有在外部的类被实例化后,这个内部类才能被实例化。需要注意的是,非静态内部类中不能有静态成员。
public class Outer {
private static int number = 100;
private int j = 20;
private String name = "Java";
public static void outer_funOne(){
System.out.println("外部类静态方法:outer_funOne");
}
public void outer_funTwo(){
}
//静态内部类可以用public、protected、private修饰
//静态内部类可以定义静态类或非静态内部类
private static class Demo{
static int j = 100;
String name = "C#";
//静态内部类里的静态方法
static void demo_funOne(){
//静态内部类只能访问外部类的静态成员(静态变量、静态方法)
System.out.println("静态内部类访问外部类静态变量:"+number);
outer_funOne();//访问外部类静态方法
}
//静态内部类非静态方法
void demo_funTwo(){
}
}
public void outer_funThree(){
//外部类访问内部类静态成员:内部类.静态成员
System.out.println(Demo.j);
//访问静态方法
Demo.demo_funOne();
//访问静态内部类的非静态成员,实例化内部类即可
Demo demo = new Demo();
//访问非静态变量
System.out.println(demo.name);
//访问非静态方法
demo.demo_funTwo();
}
public static void main(String[] args) {
new Outer().outer_funThree();
}
}
运行结果:
成员内部类
即在一个类中直接定义的内部类,成员内部类与普通类的成员没什么区别,可以与普通成员一样进行修饰和限制。成员内部类不能含有static的变量和方法。
和静态内部类非常相似,都是定义在一个类中的成员位置,与静态内部类唯一的区别是,成员内部类没有static修饰。或者也可以这么理解:我们知道一个类有成员变量、有成员方法,那么这些成员定义在类中的哪个位置,那么成员内部类也就定义在哪个位置。
public class Outer {
private static int number = 100;
private int j = 20;
private String name = "Java";
public static void outer_funOne(){
System.out.println("外部类Outer的静态方法:outer_funOne");
}
public void outer_funTwo(){
System.out.println("外部类的普通方法:outer_funTwo");
}
//成员内部类,可以访问外部类的所有成员
class Demo{
//内部类不允许定义静态变量
//static int demo_i = 100;
int j =50; //内部类和外部类的实例变量可以共存
//成员内部类中的方法定义
public void demo_funOne(){
//内部类中访问内部类自己的变量直接用变量名
//也可以用 this.j
System.out.println(j);
//内部类中访问外部类的成员变量语法:外部类类名.this.变量名
System.out.println("内部类访问外部类变量:"+Outer.this.j);
//如果内部类中没有与外部类中有相同的变量,则可以直接用变量名使用
System.out.println(name);
//内部类调用外部类方法
outer_funOne(); //静态方法
outer_funTwo(); //非静态方法
}
}
public static void outer_funThree(){
//外部类静态方法访问成员内部类
// 1、建立外部类对象
Outer out = new Outer();
// 2、根据外部类建立内部类对象
Demo demo = out.new Demo();
// 访问内部类方法
demo.demo_funOne();
//访问内部类字段
System.out.println("内部类成员字段:"+demo.j);
}
public static void main(String[] args) {
//调用内部类的方法
// 1、创建外部类对象
Outer out = new Outer();
// 2、通过外部类对象创建内部类对象
Outer.Demo demo = out.new Demo();
// 1、2步简写
// Outer.Demo demo1 = new Outer().new Demo();
//方法调用
demo.demo_funOne();
}
}
运行结果:
局部内部类
局部内部类指的是定义在一个代码块内的类,它的作用范围为其所在的代码块,是内部类中最少使用到的一种类型。局部内部类像局部变量一样,不能被public、protected、private以及static修饰。对一个静态内部类,去掉其声明中的“static”关键字,将其定义移入其外部类的静态方法或静态初始化代码段中就成为局部静态内部类。对一个成员类,将其定义移入其外部类的实例方法或实例初始化代码中就成为局部内部类。局部静态内部类与静态内部类的基本特性相同。局部内部类与内部类的基本特性相同。
public class Outer {
private static int number = 100;
private int j = 20;
private String name = "Java";
//定义外部类方法
public void outer_funOne(int k){
final int number = 100;
int j = 50;
//方法内部的类(局部内部类)
class Demo{
public Demo(int k){
demo_funOne(k);
}
int number = 300; //可以定义与外部类同名的变量
// static int j = 10; //不可以定义静态变量
//内部类的方法
public void demo_funOne(int k){
System.out.println("内部类方法:demo_funOne");
//访问外部类的变量,如果没有与内部类同名的变量,则可直接用变量名
System.out.println(name);
//访问外部类与内部类同名的变量
System.out.println(Outer.this.number);
System.out.println("内部类方法传入的参数是:"+k);
}
}
new Demo(k);
}
public static void main(String[] args) {
//访问内部类必须要先有外部类对象
Outer out = new Outer();
out.outer_funOne(11);
}
}
运行结果:
匿名内部类
匿名内部类是一种没有类名的内部类,不使用关键字class,extends和implements,没有构造方法它必须继承(Extends)其他类或实现其他接口。匿名内部类的一般好处是代码更加简洁、紧凑,但带来的问题是易读性下降。它一般应用于GUl(Graphical User Interface,图形用户界面)编程中实现事件处理等。
A、继承式的匿名内部类
public class Car {
public void drive(){
System.out.println("Driving a car!");
}
public static void main(String[] args) {
Car car = new Car(){
public void drive() {
System.out.println("Driving another car!");
}
}
car.drive();
}
}
//结果输出了:Driving another car! Car引用变量不是引用Car对象,而是Car匿名子类的对象。
B、接口式的匿名内部类
interface Vehicle {
public void drive();
}
class Test{
public static void main(String[] args) {
Vehicle v = new Vehicle(){
public void drive(){
System.out.println("Driving a car!");
}
}
v.drive();
}
}
上面的代码很怪,好像是在实例化一个接口。事实并非如此,接口式的匿名内部类是实现了一个接口的匿名类。而且只能实现一个接口。
C、参数式的匿名内部类
class Bar{
void doStuff(Foo f){
f.foo();
}
}
interface Foo{
void foo();
}
class Test{
static void go(){
Bar b = new Bar();
b.doStuff(new Foo(){
public void foo(){
System.out.println("foofy");
}
});
}
}
内部类的共性:
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号
- 内部类不能用普通的方式访问
- 内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量
- 外部类不能直接访问内部类的的成员,但可以通过内部类对象来访问
为什么需要内部类?
其主要原因有以下几点:
- 内部类方法可以访问该类定义所在的作用域的数据,包括私有的数据
- 内部类可以对同一个包中的其他类隐藏起来,一般的非内部类,是不允许有 private 与protected权限的,但内部类可以
- 可以实现多重继承
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷
使用内部类最吸引人的原因是:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。大家都知道Java只能继承一个类,它的多重继承在我们没有学习内部类之前是用接口来实现的。但使用接口有时候有很多不方便的地方。比如我们实现一个接口就必须实现它里面的所有方法。而有了内部类就不一样了。它可以使我们的类继承多个具体类或抽象类。
外部类
外部类,顾名思义,就是外部的类。定义一个类A,在A的内部再定义一个类B,则A就是外部了类,B就是内部类。
外部类与内部类的区别与联系
内部类可以访问外部类所有的方法和属性,如果内部类和外部类有相同的成员方法和成员属性,内部类的成员方法调用要优先于外部类即内部类的优先级比较高(只限于类内部,在主方法内,内部类对象不能访问外部类的成员方法和成员属性),外部类只能访问内部类的静态常量或者通过创建内部类来访问内部类的成员属性和方法。
参考文章:
Java中的内部类和外部类,内部类有哪四种类型?Java 内部类(成员内部类、局部内部类、静态内部类,匿名内部类)关于Java的一些小总结-内部类、外部类java内部类