先举个简单例子引出内部类:
当描述事务时,一个事务的内部还有事务,该事务用内部类来表示,该内部事务需要用到外部事务的内容。
例如:一个人体,它里面有各种器官:胃,肝等,拿胃来说,它要用到人体的食道,那么我们就可以把胃定义成内部类
public class Body {
private String shidao = "shidao";
/**
* 1胃是body私有的,不能让其它人直接访问,可以提供一个方法给外面
* 2这也体现了面向对象的设计思想
*/
private class Wei {
private void work() {
System.out.println("i am work through" + Body.this.shidao);
}
}
public Wei showWei() {
return new Wei();
}
}
基本概念:
- 将内部类当做是一个类中的方法,它就享有一个方法该有的东西,比如四个权限修饰符,比如局部内部类不能含有权限修饰符等
- 内部类分为成员内部类和局部内部类,成员内部类分为:静态内部类和实例内部类;局部内部类:class修饰和匿名内部类(就是直接new一个类或接口)
- 只有当类被abstract修饰时候,类中的方法才能用abstract;这个类可以是任何类型的类(除了匿名内部类)
- 内部类可以是接口和枚举,他们一直都是静态的,我们可以用他们来把一些字段归类,然后其它地方可以使用
- 实例内部类可以访问外部类的所有变量和方法,它自己不能含有任何static的东西,这与方法一样(实例方法不可能含有静态东西)
- 内部类如果定义了static成员,那么该类必定是静态内部类
- 内部类表达过程中最好把从属关系表达出来,这样有助于理解:其它地方要使用对象a的实例内部类Inner:先new:a.new Inner()
下图是内部类特点:
![](https://i-blog.csdnimg.cn/blog_migrate/0742bdece1775cf3355f4195fcf81f6f.png)
public class Outer {
private String name="tgm";
//可以有四种权限修饰符修饰,
public static class In1{}
public class In2{int in2Num=2;}
private abstract class In3{ public abstract void callMe();}
private class In4{
private String name="in4";
private void doSay(){
String name="doSay";
//内部类访问外部类东西原因就是Outer.this访问自己的就是this.
System.out.println(this.name+Outer.this.name+name);
}
}
private void userIn(){
//外部类要访问成员内部内中的东西就需要先实例化它
In2 in2=new In2();
//in2Num尽管是private的,但是它是在Outer类中,所以还是能够被访问
System.out.println(in2.in2Num);
}
}
public class TestClass {
public String name;
public static void main(String[] args) {
//实例内部类的创建方式
Outer outer=new Outer();
Outer.In2 in2= outer.new In2();
//因为in2Num权限修饰符原因,不在同包中的类不能访问
System.out.println(in2.in2Num);
//静态内部类的创建
Outer.In1 in1=new Outer.In1();
System.out.println();
}
}
内部类的用处举例:
- 有时候我们会有Constant这样的存储静态变量类,而我们可以使用内部接口或者枚举来分类:
public class Constant { public static final String CURRENT_USER = "currentUser"; public enum VerifyType { USERNAME("username"), EMAIL("email"); VerifyType(String str){ this.str=str; } private String str; public String getStr(){ return this.str; } } public interface VerifyMsg{ //接口里面的字段默认是public static final 修饰的编译常量 public static final String USERNAME_EXIST = "用户名已经存在"; String USERNAME_NOT_EXIST = "用户名不存在"; String EMAIL_EXIST = "Email已经存在"; String ERROR_TYPE = "无效的验证类型"; } }
使用方式:
Constant.VerifyType.USERNAME.getStr()
Constant.VerifyMsg.EMAIL_EXIST
- 高级用法,使用内部类作为字段来保存某个方法,然后在其它地方使用:
public class Dog { public static void main(String[] args) { Dog dog = new Dog(); dog.run(); } public void run() { DogAb dogAb=new DogAb(); try{ //这里就是引用类型参数传递,所以我们用了内部类来作为参数,用它来保存 calculate("good",dogAb); }finally { if(dogAb.callRun!=null){ dogAb.callRun.justDo(); } } } public void calculate(String type,DogAb dogAb) { Integer num=1; if(type.equals("good")){ num+=1; } if(type.equals("bad")){ num-=1; } //匿名内部类使用的都是final字段,所以要重新赋值 Integer finalNum = num; dogAb.callRun = dogAb.new CallRun(){//匿名内部类可以复写所有方法 @Override void doSomething() { sendMsg(finalNum,"http://baga:8903/kk"); } }; } public void sendMsg(int num,String url){ //这里面就需要使用上面的num,条件要求这个方法要在其它地方使用,所以这里怎么办呢?因为我们一般是直接调用方法 //解决办法是:我们先要把这个方法存起来,其它方法要用的时候再调用,怎么实现?就要用到线程和内部类 System.out.println("num is:"+num+"\nurl is:"+url); } } public class DogAb { //这里我们必须使用一个内部类,然后把这个类作为一个字段引用 //因为在我们的实际使用中牵扯到了引用类型的参数传递问题, //引用类型传递的是指向对象的栈地址,这是一个全新的变量B, //重新给它new对象并没有改变到A的指向 CallRun callRun; public class CallRun { void doSomething() { } public void justDo() { new Thread(new Runnable() { @Override public void run() { doSomething(); } }).start(); } } }