(八)接口
1.基本介绍
1.1快速入门
//定义一个接口
public interface UsbInterface {
//规定接口的相关规定
public void start();
public void stop();
}
//Phone 类 实现 UsbIterface接口
//解读 1. 即 Phone 类需要实现 UsbInterface 接口 规定/声明的方法
public class Phone implements UsbInterface{ //实现接口
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机停止工作");
}
}
// Camera类 实现 UsbIterface接口
public class Camera implements UsbInterface{//实现接口,就是把接口方法实现
@Override
public void start() {
System.out.println("相机开始工作");
}
@Override
public void stop() {
System.out.println("相机停止工作");
}
}
//Computer 使用接口
public class Computer {
//编写一个方法
public void work( UsbInterface usbInterface){
usbInterface.start();
usbInterface.stop();
}
}
public class Interface01 {
public static void main(String[] args) {
//创建手机相机对象
Phone phone = new Phone();
Camera camera = new Camera();
//创建计算机
Computer computer = new Computer();
computer.work(phone);//把手机接入到计算机
System.out.println("===============");
computer.work(camera);//吧相机接入到计算机
}
}
1.2基本介绍
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来。
语法:
深入讨论(代码演示):
public interface DBInterface {
public void connect();//开始了解
public void close();//关闭连接
}
public class OracleDB implements DBInterface{ //实现接口,重写方法
@Override
public void connect() {
System.out.println("OracleDB 开始连接");
}
@Override
public void close() {
System.out.println("OracleDB 关闭连接");
}
}
public class MysqlDB implements DBInterface{ //实现接口
@Override
public void connect() {
System.out.println("MysqlDB 开始连接");
}
@Override
public void close() {
System.out.println("MysqlDB 关闭连接");
}
}
public class Interface03 {
public static void main(String[] args) {
MysqlDB mysqlDB = new MysqlDB();
con(mysqlDB);
OracleDB oracleDB = new OracleDB();
con(oracleDB);
}
public static void con(DBInterface dbInterface){
dbInterface.connect();
dbInterface.close();
}
}
1.3注意事项和细节
接口不能被实例化
接口中所有的方法都是public方法,接口中抽象方法,可以不用abstract修饰
一个普通类实现接口,就必须实现该接口的所有方法
解释:Class ‘A’ must either be declared abstract or implement abstract method ‘cry()’ in ‘IA’ (类“A”必须声明为抽象或在“IA”中实现抽象方法“cry()”)抽象方法去实现接口时,可以不实现接口的抽象方法
一个类可以实现多个接口
- 接口中的属性只能是final ,而且是public static final 修饰符
7. 接口属性的访问形式 :接口名.属性名
- 接口不能继承其他的类,但是可以继承多个其他接口
- 接口的修饰符只能是 public 和默认,这点和类的修饰符是一样
总计:
2. 课堂练习
- 输出的类容
public class InterfaceExcrcise01 {
public static void main(String[] args) {
BBB bbb = new BBB();
System.out.println(bbb.a);
System.out.println(AAA.a);
System.out.println(BBB.a);
}
}
interface AAA{
int a = 23;
}
class BBB implements AAA{
}
3.实现接口 vs继承类
接口和继承解决的问题不同
继承的主要价值在于 :解决代码的可复用性和耦合性;
接口的主要价值在于:设计 设计好各种规范(方法),让其他类去实现这些方法,即变得更加得灵活。
接口比继承更加灵活
接口比继承更加灵活,继承是满足is~a的关系,而接口只需满足like-a的关系。接口一定程度上实现代码解耦。[即接口规范性+动态绑定机制]
举例:
小猴子继承于Monkey 拥有父类非私有的属性和方法,但是小猴子想飞或者游泳。这时候可以把飞,和游泳定义为接口,这时小猴子就可以去实现(implement)这两个接口达到飞和游泳的技能。(这相当于后天学习)
public class ExtendsVsInterface {
public static void main(String[] args) {
LittleMonkey wuKong = new LittleMonkey("悟空");
wuKong.climbing();
wuKong.swimming();
}
}
//小结:当子类继承了父类。就自动的拥有父类的功能
//如果子类需要扩展功能,可以通过实现接口的方式扩展
//可以理解 实现接口 对Java 单继承机制的一种补充
class Monkey {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Monkey(String name) {
this.name = name;
}
public void climbing() {
System.out.println(name + " 会爬树");
}
}
interface FishAble {
void swimming();
}
interface BirdAble {
void fly();
}
class LittleMonkey extends Monkey implements FishAble, BirdAble {
public LittleMonkey(String name) {
super(name);
}
@Override
public void swimming() {
System.out.println(getName() + " 经过学习会游泳");
}
@Override
public void fly() {
System.out.println(getName() + "经过学习会飞翔");
}
}
4.接口的多态特性
public class InterfacePolyParameter {
public static void main(String[] args) {
//接口的多态
//接口类型的变量 if01 可以指向 实现了IF接口的对象实例
IF if01 = new Monster();
if01 = new Car();
//继承体现的多态
//父类类型的变量 a 可以指向 继承了AAAA的对象实例
AAAA a = new BBBB();
a = new CCCC();
}
}
interface IF{}//接口
class Monster implements IF{}//实现类Monster
class Car implements IF{}//实现类Car
class AAAA{}//父类
class BBBB extends AAAA{}//子类BBBB
class CCCC extends AAAA{}//子类CCCC
多态数组 -> 接口类型数组
记一记:(instanceof)判断对象运行类型返回值true/false
public class InterfacePolyArr {
public static void main(String[] args) {
//多态数组 -> 接口类型数组
Usb[] usb = new Usb[2];
usb[0] = new Phone_();
usb[1] = new Camera_();
/*给 Usb 数组中,存放 Phone 和 相机对象,Phone 类还有一个特有的方法 call(),
请遍历 Usb 数组,如果是 Phone 对象,除了调用 Usb 接口定义的方法外,
还需要调用 Phone 特有方法 call */
for (int i = 0; i < usb.length; i++) {
usb[i].work();;//动态绑定..
// 和前面一样,我们仍然需要进行类型的向下转型
if (usb[i] instanceof Phone_){//判断他的运行类型是 Phone_
((Phone_) usb[i]).call();
}
}
}
}
interface Usb{
void work();
}
class Phone_ implements Usb{
@Override
public void work() {
System.out.println("手机在工作。。。");
}
public void call(){
System.out.println("手机可以打电话");
}
}
class Camera_ implements Usb{
@Override
public void work() {
System.out.println("相机在工作。。。");
}
}
接口存在多态传递现象。InterfacePolyPass.java
public class InterfacePolyPass {
public static void main(String[] args) {
//接口类型的变量可以指向,实现了该接口的类的对象实例
IG ig = new Teacher();
//如果IG继承了IH 而Teacher实现了IG接口
//那么也相当于 Teacher实现了IH接口
//这就是所谓得接口多态传递现象
//(这和子类继承父类,父类继承了爷爷类一样 爷爷类就可以指向子类了)
IH ih = new Teacher();
}
}
interface IG extends IH{}
interface IH{}
class Teacher implements IG{}
5.课堂练习 InterfaceExercise02.java
public class InterfaceExercise02 {
public static void main(String[] args) {
new Cc().pX();
}
}
interface Aa{
int x = 0;
//等价 public static final int x = 0;
}
class Bb{
int x =1;
//普通变量
}
class Cc extends Bb implements Aa{
public void pX(){
//System.out.println(x); //错误,原因:x变量指向不明确
//可以明确的指定 x
// 访问接口的 x 就使用 A.x
// 访问父类的 x 就使用 super.x
System.out.println(Aa.x + " " + super.x);
}
}
(九)内部类
1.基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(Outer class)。是类的第五大成员(属性,方法,构造器,代码块,内部类)。内部类最大的特点是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
1.1基本语法
public class InnerClass01 {
//外部其他类
public static void main(String[] args) {
}
}
class Outer { //外部类
private int n1 = 100;//属性
public Outer(int n1) {//构造器
this.n1 = n1;
}
public void m1() {//方法
System.out.println("m1()");
}
{//代码块
System.out.println("代码块...");
}
class Inner { //内部类, 在 Outer 类的内部
}
}
2.内部类的分类
定义在外部类的局部位置上(比如方法内)
- 局部内部类(有类名)
- 匿名内部类(没有类名 重点!)
定义在外部类的成员位置上:
- 成员内部类(没有static修饰)
- 静态内部类(使用static修饰)
3.局部内部类
3.1局部内部类的使用
**说明:**局部内部类是定义在外部类的局部位置 通常在方法中
- 可以直接访问外部类所有成员,包含私有的
- 不能添加访问修饰符,因为他的地位是一个局部变量。局部变量不能用修饰符修饰,大师可以使用final修饰,因为局部变量也可以用final
- 作用域:仅仅在定义他的方法或者代码块中
- 局部内部类可以直接访问外部类成员,(就比如下面外部类n1和m2())
- 外部类在方法中,可以创建内部类对象实例,然后调用方法即可(必须在作用域类)
记住:(1) 局部内部类定义在方法中/代码块
(2)作用域在方法体或者在代码块中
(3)本质认识一个类
外部类不能访问局部内部类(因为局部内部类的地位是一个局部变量)
如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问 System.out.println(“外部类的n2:” +外部类名.this.成员);
实例:
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02 {
private int n1 = 10;
private int n2 = 20;
public void m2() {
System.out.println("m2");
}
public void m1() {
//1.局部内部类是定义在外部类的局部位置 通常在方法中
//3.不能添加访问修饰符,因为他的地位是一个局部变量。局部变量不能用修饰符修饰,但是可以使用final修饰
//4.作用域:仅仅在定义他的方法或者代码块中
class Inner {//局部内部类(本质任然是一个类)
String name = "小南门";
private int n1 = 200;
//2.可以直接访问外部类所有成员,包含私有的
public void f1() {
//5.局部内部类可以直接访问外部类成员,就比如下面外部类n1和m2()
//7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成
//员,则可以使用(外部类名.this.成员)去访问
// System.out.println("外部类的n2:" +外部类名.this.n2);
//解读:Outer.this 本质就是外部类的一个对象,即那个对象调用了m1(),Outer.this就是那个对象
System.out.println("n1 =" + n1);
m2();
System.out.println(Outer02.this.n1);
}
}
//6.外部类在方法中,可以创建Inner02对象实例,然后调用方法即可
Inner inner = new Inner();
inner.f1();
}
}
3.2扩充:什么是局部变量和全局变量:
- 局部变量:
局部变量的定义:定义在方法中的变量都是局部变量(main方法也是方法,所以定义在main方法中的变量也是局部变量)。
生存时间:局部变量的生存时间和方法的生存时间一致,调用该方法声明该局部变量并初始化的时,该局部变量被创建并分配内存空间;直到该方法调用结束局部变量也就结束了;
是否需要初始化:局部变量在使用前必须进行初始化,系统默认不会对局部变量进行初始化数据操作,如果局部变量在使用前没有进行初始化则会在编译器报错;如果局部变量进行了声明没有进行初始化,但是也一直没有被使用的话编译也是不会报错的;(局部变量使用前必须初始化话)
创建位置: 局部变量是创建在栈内存中的;
全局变量:
2.1 非静态全局变量:
非静态全局变量的定义:非静态全局变量都是定在类中,是类的成员变量或者说是成员属性属于类的一部分(或者说是对象的一部分); 生存时间:非静态全局变量加载在堆内存中,随着声明初始化而创建,随着对象消亡而消亡; 是否需要初始化:全局变量都是不需要被强制初始化的,系统都会默认根据其数据类型进行默认赋值;但是建议在声明时都进行初始化操作; 创建位置:创建在堆内存中,因为非静态的全局变量数对象的成员变量是对象的一部分;
2.2静态全局变量:
静态全局变量的定义:静态的类成员变量; 生存时间:静态全局变量随着类的字节码文件加载而加载产生,随着字节码文件的消失而消失,生存时间比类的对象还要长; 是否初始化:凡是全局变量都是可以不要初始化的,静态变量也是一样,系统会自动根据其数据类型进行赋默认值,但是建议变量在声明时都进行初始化; 创建位置:静态变量时存在于堆内存中的,所以静态全局变量也是存在于堆内存中的;
4.匿名内部类
4.1匿名内部类的使用
本质是类 2.内部类 3. 该类没有名字 4.同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
代码演示:
基于接口的匿名内部类:
/**
* 演示匿名内部类
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 {//外部类
private int n1 = 10;//属性
public void method() {//方法
//基于接口的匿名内部类
// 老韩解读
// 1.需求: 想使用 IA 接口,并创建对象
// 2.传统方式,是写一个类,实现该接口,并创建对象
//3.需求是:Tiger类只是用一次,后面不在使用
//4.可以使用匿名内部类来简化开发
//5.tiger1 的编译类型 ? IA
//6.tiger1 的运行类型 ? 就是匿名内部类 Outer04$1
/*
看底层 会分配 类名 Outer04$1
class Outer04$1 implement IA{
@Override
public void cry() {
System.out.println("老虎哭了");
}
}
*/
//7.jdk底层在创建内部类 Outer04$1时,就马上创建了 Outer04$1实例,并且把地址返回给tiger1
//8.匿名内部类使用一次,就不能在使用 但是对象还在
IA tiger1 = new IA() {
@Override
public void cry() {
System.out.println("老虎叫了");
}
};
tiger1.cry();
System.out.println("tiger1 = " + tiger1.getClass());//查看运行类型
Tiger tiger = new Tiger();
tiger.cry();
}
}
interface IA { //接口
void cry();
}
class Tiger implements IA {
@Override
public void cry() {
System.out.println("老虎哭了");
}
}
class Father {//类
public Father(String name) {//构造器
}
public void test() {
}
}
基于类的匿名内部类:
//演示基于类的匿名内部类
// 分析
// 1. father 编译类型 Father
// 2. father 运行类型 Outer04$2
// 3. 底层会创建匿名内部类
/*
class Outer04$2 extends Father{//类
public Father(String name) {//构造器
}
public void test() {
}
}
*/
//4.同时也直接返回了匿名内部类Outer$2的对象
//5.注意("jack")参数会传递给 构造器
Father father = new Father("jack"){
@Override
public void test() {
System.out.println("匿名内部类重写了test()");
}
};
System.out.println("father运行类型" + father.getClass());//Outer04$2
father.test();
//基于抽象类的匿名内部类
Animal animal = new Animal(){
@Override
void eat() {
System.out.println("小狗吃饭");
}
};
animal.eat();
}
}
class Father {//类
public Father(String name) {//构造器
System.out.println("接受到name = " + name);
}
public void test() {
}
}
abstract class Animal{
abstract void eat();
}
- 可以直接访问外部类的所有成员,包括私有的
- 不能添加访问修饰符,因为他的地位就是一个局部变量。
- 作用域:仅仅在定义他的代码块或者方法中
- 外部其类不能访问匿名内部类(因为 匿名内部类地位是一个局部变量)
- 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
public class AnonymousInnerClassDetail01 {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
System.out.println("main 中outer05的hashcode = " + outer05);
}
}
class Outer05 {
private int n1 = 9;
public void f1() {
//创建一个基于类的匿名内部类
//4.不能添加访问修饰符,因为他的地位就是一个局部变量。
//5.作用域:仅仅在定义他的代码块或者方法中
//6.外部其类不能访问匿名内部类(因为 匿名内部类地位是一个局部变量)
Person person = new Person() {
private int n1 = 11;
@Override
public void hi() {
//4.可以直接访问外部类的所有成员,包括私有的
//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
System.out.println("匿名内部类重写了hi() n1 = " + Outer05.this.n1);
//Outer.this 就是调用 f1的对象
System.out.println("Outer05.this的hashcode = " + Outer05.this);
}
};
person.hi();//动态绑定 运行类型时Outer05$1
//3.也可以直接调用,匿名内部类本身也是返回对象
//class 匿名内部类 extends Person {}
new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写了hi() new Person");
}
@Override
public void ok(String str) {
super.ok(str);
}
}.ok("jack");
}
}
class Person {
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str) {
System.out.println("Person ok() " + str);
}
}
4.2匿名内部类最佳实战
- 当作实参直接传递,简洁高效
public class InnerClassExercise01 {
public static void main(String[] args) {
//当作实参直接传递,简洁高效
f1(new IL() {
@Override
public void show() {
System.out.println("这是一副名画");
}
});
//传统方式
Picture picture = new Picture();
f1(picture);
}
//静态方法,形参是接口类型
public static void f1(IL il){
il.show();
}
}
interface IL{//接口
void show();
}
//类->实现 IL => 编程领域 (硬编码)
class Picture implements IL{
@Override
public void show() {
System.out.println("这是一副名画");
}
}
4.3匿名内部类课堂练习
/*
1.有一个铃声接口Bell,里面有个ring方法。
(右图)
2.有一个手机类Cellphone,具有闹钟功能
alarmclock,参数是Bell类型(右图)
3.测试手机类的闹钟功能,通过匿名内部类(对
象)作为参数,打印:懒猪起床了
4.再传入另一个匿名内部类(对象),打印:
小伙伴上课了
*/
public class InnerClassExercise02 {
public static void main(String[] args) {
CellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
CellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪上课了");
}
});
}
}
interface Bell { //接口
void ring();//抽象方法
}
class CellPhone {
public static void alarmClock(Bell bell) {//形参时Bill接口类型
bell.ring();//动态绑定
};
}
5.成员内部类的使用
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。有static称为静态内部类。
注意:成员内部类,是定义在外部类的成员位置上
可以添加任意的访问修饰符(public protected,默认 ,private),因为他的地位就是一个成员变量
作用l域MemberlnnerClass01,java和外部类的其他成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法.
成员内部类–>访问–>外部类成员(比如:属性)[访问方式:直接访问]
外部类–访问—>成员内部类访问方式:创建对象,再访问
外部其他类–访问–>成员内部类 有两种方式
//第一种方式
//outer08.new Inner08(); 相当与new Inner08() 当作是 outer08的成员
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
//第二种方式 在外部类中编写一个方法 可以返回Inner08对象
Outer08.Inner08 inner081 = outer08.getInner08();
inner081.say();
public Inner08 getInner08(){
return new Inner08();
}
代码演示:
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
//外部其他类,使用成员内部类的三种方式
//第一种方式
//outer08.new Inner08(); 相当与new Inner08() 当作是 outer08的成员
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
//第二种方式 在外部类中编写一个方法 可以返回Inner08对象
Outer08.Inner08 inner081 = outer08.getInner08();
inner081.say();
}
}
class Outer08 {//外部类
private int n1 = 8;
public String name = "张三";
//1.注意:成员内部类,是定义在外部类的成员位置上
//2.可以添加任意的访问修饰符(public protected,默认 ,private),因为他的地位就是一个成员变量
public class Inner08 {//成员内部类
private double sal = 9.99;
private int n1 = 100;
public void say() {
//1.可以直接访问外部类的其他成员,包括私有的
System.out.println(Outer08.this.n1 + name);
}
}
public Inner08 getInner08() {
return new Inner08();
}
//写方法
public void t1() {
//使用成员内部类
//创建成员内部类的对象,然后使用相关的方法
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println(inner08.sal);
}
6.静态内部类的使用
放在外部类的成员位置 使用static修饰
可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
作用域:同其他的成员,为整个类体
静态内部类–访问–>外部类(比如:静态属性)[访问方式:直接访问所有静态成员]
外部类–访问–>静态内部类访问方式:创建对象,再访问
外部其他类,使用静态内部类 有两种方法
//外部其他类,使用静态内部类 //方式一 //因为静态内部类,是可以直接通过类名访问(前提时满足访问权限) Outer10.Inner10 inner10 = new Outer10.Inner10(); inner10.say(); //方法二 //编写一个方法,可以返回静态内部类的对象实例 Outer10.Inner10 inner101 = outer10.getInner10(); inner101.say(); //静态,可以直接用类名点方法调用 Outer10.Inner10 inner102 = new Outer10().getInner10(); inner102.say();
如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问.
小结:
内部类有四种:局部内部类,匿名内部类,成员内部类,静态内部类
重点掌握匿名内部类
new 类/接口(参数列表){
//…
}
- 成员内部类,静态内部类是放在外部类的成员位置,本质就是一个成员
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类,使用静态内部类
//方式一
//因为静态内部类,是可以直接通过类名访问(前提时满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方法二
//编写一个方法,可以返回静态内部类的对象实例
Outer10.Inner10 inner101 = outer10.getInner10();
inner101.say();
//静态,可以直接用类名点方法调用
Outer10.Inner10 inner102 = new Outer10().getInner10();
inner102.say();
}
}
class Outer10 { //外部类
private int n1 = 10;
private static String name = "张三";
//Inner10 就是一个静态内部类
//1.放在外部类的成员位置
//2.使用static修饰
//1.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//2.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
//3.作用域:同其他的成员,为整个类体
//4.静态内部类--访问-->外部类(比如:静态属性)[访问方式:直接访问所有静态成员]
//5.外部类--访问-->静态内部类访问方式:创建对象,再访问
//6.外部其他类,使用静态内部类 有两种方法
//7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如
//果想访问外部类的成员,则可以使用(外部类名.成员)去访问.
static class Inner10 {
private static String name = "覃波";
public void say() {
System.out.println(name);
System.out.println(Outer10.name);
}
}
public void m1(){//外部类--访问-->静态内部类访问方式:创建对象,再访问
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10(){
return new Inner10();
}
}
7.内部类练习
/**
*写出输出结果
*/
public class InnerClassExercise03 {
}
class Test {//外部类
public Test() {//构造器
Inner s1 = new Inner();
s1.a = 10;
Inner s2 = new Inner();
System.out.println(s2.a);
}
class Inner { //内部类,成员内部类
public int a = 5;
}
public static void main(String[] args) {
Test t = new Test();
Inner r = t.new Inner();//5
System.out.println(r.a);//5
我亦无他,惟手熟尔