内部类
一个类A定义在另一个类B的内部,则A就称为类B的内部类,B为类A的外部类。一般两个类之间有包含关系,但是又是独立的个体时,可以使用内部类的方式;例如小汽车和引擎的关系,就可以将引擎类作为小汽车类的内部类。
1、一般内部类
先来看如下的代码:
public class BMWCar {
private String origin;
public BMWCar(String origin) {
this.origin = origin;
}
public void carInfo() {
Engine engine=this.new Engine("8848.1v", "fort");// 也可以直接new Engine("8848.1v", "fort");
System.out.println(String.format("当前汽车产地:%s;引擎型号为:%s;引擎厂商为:%s",origin,engine.type,engine.company));
}
class Engine{
private String NAME="engine";
private String type;
private String company;
public Engine(String type, String company) {
this.type = type;
this.company = company;
}
public Engine() {
}
public void judgeEngineOrigin() {
if ("USA".equals(origin)) {
System.out.println("汽车引擎产自美国!");
}
if ("Germany".equals(origin)) {
System.out.println("汽车引擎产自德国!");
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getNAME() {
return NAME;
}
public void setNAME(String nAME) {
NAME = nAME;
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
BMWCar bmwCar=new BMWCar("USA");
bmwCar.carInfo();
bmwCar.new Engine().judgeEngineOrigin();
System.out.println(bmwCar.new Engine().getNAME());
}
}
输出结果:
当前汽车产地:USA;引擎型号为:8848.1v;引擎厂商为:fort
汽车引擎产自美国!
engine
总结一般内部类的特点:
1、内部类可以直接访问外部类的成员,包括私有成员。
2、外部类要访问内部类的成员,必须要建立内部类的对象,创建格式如下:
【外部类名.内部类名 对象名 = new 外部类型().new 内部类型();】
3、在内部类所属的外部类中所创建的内部类对象可以直接调用私有属性,而不用使用get方法,但是其他的 外部类则必须使用get方法。
2、静态内部类
现在对上边的代码进行修改,将内部类改为静态的:
public class BMWCar {
private static String origin="USA"; // 这个字段必须为静态的,否则静态内部类不能直接调用
// private String origin1="USA";
public void carInfo() {
Engine engine=new Engine("8848.1v", "fort"); // 这里不能使用this.new Engine(...)
System.out.println(String.format("当前汽车产地:%s;引擎型号为:%s;引擎厂商为:%s",origin,engine.type,engine.company));
}
static class Engine{
public void judgeEngineOrigin() {
Jvm_Mat_Analyzer analyzer=new Jvm_Mat_Analyzer();
OOMBean oomBean=analyzer.new OOMBean("cc",1);
System.out.println(oomBean.getDate());
if ("USA".equals(origin)) { //也可以使用 new BMWCar().origin1
System.out.println("汽车引擎产自美国!");
}
if ("Germany".equals(origin)) {
System.out.println("汽车引擎产自德国!");
}
}
// 其他内容与原来的一样,这里省略掉
}
}
测试类:
import xxx.BMWCar.Engine;
public class Test {
public static void main(String[] args) {
BMWCar bmwCar=new BMWCar();
bmwCar.carInfo();
// 这里使用创建一般类对象的方式来创建,但是需要使用import将Engine类的路径导入
// 或者不导入Engine类,使用:BMWCar.Engine engine=new BMWCar.Engine();的方式
Engine engine=new Engine();
engine.judgeEngineOrigin();
System.out.println(engine.getNAME());
}
}
结果:
当前汽车产地:USA;引擎型号为:8848.1v;引擎厂商为:fort
汽车引擎产自美国!
engine
静态内部类总结:
1、静态内部类虽然是内部类,但是它只是借助于当前外部类的外壳进行类的创建,不属于任何当前外部类的对象;
2、静态内部类只能使用当前所在的外部类的静态变量,或者通过所在外部类的对象来引用,如上述代码中的new BMWCar().origin1的情况。
3、静态内部类使用时与一般的外部类没什么差别,直接使用new来创建、使用即可。
3、匿名内部类
匿名内部类 :它的本质是一个带具体实现的 父类或者父接口的 匿名的 子类对象。
以接口举例,当你使用一个接口时,似乎得做如下几步操作:
- 定义子类
- 重写接口中的方法
- 创建子类对象
- 调用重写后的方法
我们的目的,最终只是为了调用方法,匿名内部类就是实现将以上四步合为一步的操作
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
匿名内部类在接口的使用,在博客Lambda表达式在函数式接口中的使用有使用相关的介绍。这里就不赘述了。
这里说一个在父类中的使用,重写上述2中的测试方法
public class Test{
public static void main(String[] args) {
// 重写类中的方法,BMWCar必须为能够继承的类(不能为final)或者是一个【抽象类】
BMWCar bmwCar = new BMWCar() {
@Override
public void carInfo() {
System.out.println("vvvvvv");
}
};
bmwCar.carInfo();
Engine engine=new Engine();
engine.judgeEngineOrigin();
System.out.println(engine.getNAME());
}
}
执行结果如下:
vvvvvv
汽车引擎产自美国!
engine
4、常见问题
在一个json数据序列化为一个包含内部类的实例化对象时抛出异常:
non-static inner classes like this can only by instantiated using default, no-argument constructor
解决办法: 将内部类改为静态内部类即可。
原因分析: 一般内部类,编译后会看到里面多了一个指向外部类的引用(因为一般内部类无法直接进行实例化,进行实例化时需要借助于外部类的实例化引用才能完成。),但是在对其json数据进行反序列化时,会找不到这个外部类,因此会抛出异常。
参考文章:json反序列化内部类报错