在 Java 中内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类;
简单理解:
我们在描述A事物的时候,发现描述的A事物内部还存在另外一个比较复杂的事物B时候,而且这个比较复杂事物B还需要访问A事物的属性等数据,那么这时候我们就可以使用内部类描述B事物。
比如: 人—>心脏
class 人{
血
氧气
等....
//心脏是直接可以访问血、氧气等,这就解释了内部类可以访问外部类的所有数据;
class 心脏{ //好处:内部类可以直接访问外部类的所有成员(尽管是private)。
……
}
}
内部类通用法则:
- 如果外部类与内部类存在同名的成员变量时,在内部类中默认访问内部类的成员变量。可以通过"外部类.this.成员变量名" 指定访问外部类的成员。
- 内部类是private,只能在外部类提供一个方法创建内部类的对象进行访问,不能在其他类创建对象了。
- 内部类一旦出现了静态(static)的成员,那么该类也必须使用static修饰。
内部类特性:
1、内部类经过编译后的class文件名:外部类$内部类。这样做的好处是便于区分该class文件是属于哪个外部类的。
内部类的访问格式:
A:当内部类定义在外部类的成员位置,而且非私有,则可以在其他外部类中直接建立内部类对象
格式:外部类名.内部类名 内部类对象名 = new 外部类对象().内部类对象()
如:Outer.Inner in = new Outer().new Inner();
B:当内部类在外部类成员位置,且被static修饰时
1)外部其他类可直接访问静态内部类的非静态成员
格式:new 外部类名.内部类名().内部类成员
如:new Outer.Inner().function();
2)外部其他类可直接访问静态内部类的静态成员
格式:new 外部类名.内部类名.内部类成员
如:new Outer.Inner.function();
成员内部类
成员内部类法则:
- 成员内部类中不能存在static方法, 但是可以存在static域, 前提是需要使用final关键字进行修饰.
- 成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
成员内部类,调用外部类方法、变量
public class Main {
private String outString = "Out Class Variable"; //外部类的变量str
public void outMethod() {
System.out.println("外部类方法!");
}
//成员内部类
public class InnerClass {
public void innerMethod() {
System.out.print("在内部类方法中使用外部类成员变量,outString:");
System.out.println(outString);
System.out.print("在内部类方法中调用外部类成员方法:");
outMethod();
}
}
//获取内部成员,就像是类成员变量的getter和setter方法一样;
public InnerClass getInnerClass() {
InnerClass inClass = new InnerClass();
//功能代码
return inClass;
}
public static void main(String args[]) {
Main outer = new Main(); //创建外部类对象
Main.InnerClass inner = outer.getInnerClass(); //通过外部类对象调用getter方法获取内部类对象
inner.innerMethod(); //调用内部类方法
}
}
/* 输出:
在内部类方法中使用外部类成员变量,outString:Out Class Variable
在内部类方法中调用外部类成员方法:外部类方法!
*/
局部内部类
局部内部类,定义在方法里;
仔细阅读以下代码,体会局部内部类!
//功能:在一个方法内部定义一个类,然后调用run方法与sleep方法;
class Outer {
public void print() {
class Dog {
public void run() {
System.out.println("狗在跑。。。");
}
public void sleep() {
System.out.println("狗在睡觉。。。");
}
}
Dog d = new Dog();
d.run();
d.sleep();
}
}
public class Main {
public static void main(String args[]) {
Outer out = new Outer();
out.print();
}
}
/* 输出:
狗在跑。。。
狗在睡觉。。。
*/
局部内部类中变量的生命周期:
class Outer{
String name= "外部类的name";
public void test(){
final int y =100; //y什么时候从内存中消失?方法执行完毕之后y消失。
//局部内部类
class Inner{
/*
你务必要仔细看这段注释:
当test方法执行完毕之后,那么y马上从内存中消失,而Inner对象在方法执行完毕的时候
还没有从内存中消失,而inner对象的print方法还在访问着y变量,这时候的y变量已经消失了,
那么就给人感觉y的生命变量已经被延长了。这是怎么回事?
Java的解决方案: 如果一个局部内部类访问一个局部变量的时候,那么就让该局部内部类
访问这个局部 变量 的复制品。
*/
int x = 10;
public void print(){
//如果局部内部类访问了一个局部变量,那么该局部变量必须使用final修饰
System.out.println("这个是局部内部类的print方法.."+y); //这里访问的其实是y的复制品;
}
}
Inner inner = new Inner(); //这个inner对象什么时候消失?Inner对象的生命周期比局部变量y的生命周期要长。
inner.print();
}
}
public class Main
{
public static void main(String[] args)
{
Outer outer = new Outer();
outer.test();
}
}
匿名内部类
匿名内部类Syntax:
- 没有类名的类就称作为匿名内部类。
- 使用前提:必须存在继承或者实现关系才能使用。
匿名内部类特点:
1、只是没有类名,其他的一概成员都是具备的。
2、假设匿名内部类与Animal是继承的关系。可以这样想象:
class 空 extends Animal() {}
但是在new对象时没有可写类名,怎么办呢?于是就直接写成父类的类名:Animal() {}; 不管它能不能实例化;
class Outer{
public void print(){
new Animal() { //是创建Animal子类的对象. //这里十分难以理解,一定要耐心思考;
//匿名内部的成员
public Animal run(){ //实现抽象方法;
System.out.println("狗在跑..");
return this;
}
public void sleep(){
System.out.println("狗趴在睁开眼睛睡..");
}
public void bite(){//特有的方法
System.out.println("狗在咬人..");
}
}.run(); //因为没有类名,故这样调用,且只可以调用一个方法;
//如果把匿名类实例化一个对象出来!把上面的类修改为:
/*Animal a=new Animal(){
public Animal run(){ //实现抽象方法;
System.out.println("狗在跑..");
return this;
}
public void sleep(){
System.out.println("狗趴在睁开眼睛睡..");
}
public void bite(){ //特有的方法
System.out.println("狗在咬人..");
}
};
a.run();//可调用多个方法;
a.sleep();
a.bite(); 特有方法不可调用!!!那么只能有局部内部类;
*/
}
}
class JavaMain
{
public static void main(String[] args)
{
Outer outer = new Outer();
outer.print();
}
}
使用匿名内部类应该注意:
a)匿名内部类不能有构造方法
b)匿名内部类不能定义任何静态成员、方法和类。
c)匿名内部类不能是public,protected,private,static。
d)只能创建匿名内部类的一个实例。
e)一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
f)因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
静态内部类
静态内部类中可以写哪些内容
1)匿名代码块
2)静态代码块
3)静态变量和非静态变量
4)静态方法和非静态方法
外部类如何调用静态内部类中的属性和方法
1)外部类可以通过创建静态内部类实例的方法来调用静态内部类的非静态属性和方法
2)外部类可以直接通过“ 外部类.内部类.属性(方法)” 的方式直接调用静态内部类中的静态属性和方法
静态内部类如何调用外部类的属性和方法
1)静态内部类可以直接调用外部类的静态属性和方法
2)静态内部类可以通过创建外部类实例的方法调用外部类的非静态属性和方法
如何创建静态内部类实例
1)在非外部类中:外部类名.内部类名 name = new 外部类名.内部类名();
2)在外部类中:内部类名 name = new 内部类名();
尝试一下:
初次尝试
public class Outer {
//定义一个实例变量和一个静态变量
private int a;
private static int b;
//定义一个静态方法和一个非静态方法
public static void say(){}
public void test(){
//在外部类中调用内部类的属性和方法
Outer.Inner.c = 1; //可以通过静态内部类的全类名来调用静态内部类的静态属性(外部类名.静态内部类名.属性)
Outer.Inner.go(); //可以通过静态内部类的全类名来调用静态内部类的静态方法(外部类名.静态内部类名.方法)
//Outer.Inner.walk(); //不能通过类静态内部类的全类名来调用内部类的非静态属性和方法
Inner inner = new Inner();
inner.d = 1;
inner.walk(); //可以通过创建内部类实例来调用静态内部类的非静态属性和方法
}
//静态内部类
public static class Inner{
//在静态内部类中定义一个静态变量和一个实例变量
static int c;
int d;
//定义一个匿名代码块和一个静态代码块
{}
static{}
//定义一个静态方法和一个普通方法
public static void go(){}
public void walk(){
//在静态内部类中调用外部类的属性和方法
int f = b; //可以直接调用外部类的静态属性
say(); //可以直接调用外部类的静态方法
//int e = a; 直接调用外部类的非静态属性出错编译出错
//test(); 直接调用外部类的非静态方法时编译出错
Outer outer = new Outer();
int e = outer.a; //可以通过创建外部类实例来调用外部类的非静态属性
outer.test(); //可以通过创建外部类实例来调用外部类的非静态方法
}
}
}