Java-day 26
目录
访问控制
对象中的属性和方法,在被访问的时候,是可以根据类中声明这些属性和方法的时候所用的修饰符来进行控制的。对这些属性和方法的访问控制,就是控制他们可以在什么地方被访问,在什么地方不能被访问,四种修饰符表示可以被访问的范围从大到小,依次是:
public > protected > default > private
类 | 同包同类 | 同包子类 | 同包非子类 | 不同包子类 | 不同包非子类 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
注意,在做以上测试的时候,如果是在子类中,那么就直接访问的父类中继承过来这个四个属性,如果不是在子类中,那么就先创建出Person类型的对象,然后使用对象去访问这个四个属性,最终得到以上结果。
内部类
内部类不是在一个Java源文件中编写两个平行的类,而是在一个类的内部再定义另外一个类。
【不属于内部类】例如:
//Person.java文件中
//虽然这个俩个类在一个文件中,但是它们是平行的关系
//所以这种情况不是内部类
public class Person{
}
class PersonTest{
}
【属于内部类】例如:
//Person.java文件中
public class Person{
//这个就是一个内部类
//类A的是定义在Person类中的
public class A{
}
}
注意,内部类是类和类之间的嵌套关系,我们可以把定义在外面的类叫做外部类,嵌套定义在里面的类叫做内部类。Java中的内部类,可以分为四种:成员内部类,静态内部类,局部内部类,匿名内部类
通过以下几个方法,对内部类进行学习了解:
1、成员内部类
成员内部类例子
//MemberOutterClass是当前外部类
public class MemberOutterClass{
/* 成员内部类 声明开始 */
public class MemberInnerClass{
}
/* 成员内部类 声明结束 */
}
编译后的class文件:
当前这个情况,编译成功后,会生成俩个class文件,一个对象外部类,一个对应内部类class文件的名字为:
MemberOutterClass.class
MemberOutterClass$MemberInnerClass.class
定义属性、方法、构造器
//例如:
public class MemberOutterClass{
/* 成员内部类 声明开始 */
public class MemberInnerClass{
private String name;
private int age;
public void run(){}
}
/* 成员内部类 声明结束 */
}
注意,成员内部类中,【不能】编写静态的属性和方法,成员内部类中,可以编写构造器,如果有需要的话,如果不编写的话,那么默认使用无参的构造器。
成员内部类和外部类的相互访问
public class MemberOutterClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void test(){
//在外部类中,任何访问成员内部类中的属性和方法
//MemberInnerClass mic = this.new MemberInnerClass();
//在外部类中,可以把 this. 省去
//这个意思就是成员内部类对象的创建,是需要依托于【外部类对象】的
//就像一个类中的成员属性、成员方法,需要依托于这个类的对象才能访问
MemberInnerClass mic = new MemberInnerClass();
System.out.println(mic.name);
System.out.println(mic.age);
mic.run();
}
/* 成员内部类 声明开始 */
public class MemberInnerClass{
private String name;
private int age;
public void run(){}
public void test(String name){
//这个方法的输参数name
System.out.println(name);
//访问当前内部类中name属性
System.out.println(this.name);
System.out.println(MemberInnerClass.this.name);
//访问外部类中的name属性(非静态)
System.out.println(MemberOutterClass.this.name);
//访问外部类中的age属性(静态)
System.out.println(MemberOutterClass.age);
//访问外部类中的run方法(非静态)
MemberOutterClass.this.run();
//访问外部类中的go方法(静态)
MemberOutterClass.go();
//访问内部类中自己的run方法
MemberInnerClass.this.run();
}
}
/* 成员内部类 声明结束 */
}
注意,在内部类中,可以使用 【类名.this】 的形式,当前使用的this指的是哪一个类中的this,在外部类中,要访问成员内部类中的属性和方法,需要先创建这个成员内部类的对象,成员内部类对象,是要依托于外部类对象,也就是一定先创建了外部类对象,然后才能创建这个成员内部类对象。
在其他类中使用成员内部类
导入方式
import com.zzb.day26.MemberOutterClass.MemberInnerClass;
调用方式
MemberInnerClass mic = new MemberOutterClass().new MemberInnerClass();
mic.run();
运行截图
2、静态内部类
格式:
//静态内部类
public class StaticOutterClass{
/*声明开始*/
public static class StaticInnerClass{
}
/*声明结束*/
}
编译后的class文件:
StaticOutter$StaticInnerClass.class
StaticOutterClass.class
定义属性、方法、构造器
//静态内部类
public class StaticOutterClass{
/*声明开始*/
public static class StaticInnerClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
}
/*声明结束*/
}
注意,静态内部类中,【可以编写静态的属性和方法】(其他都不可以),静态内部类中,可以编写构造器,如果有需要的话,如果不编写的话,那么默认使用无参的构造器。
内部类和外部类相互访问
//静态内部类
public class StaticOutterClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void test(){
//在外部类中,访问静态内部类中的静态属性和方法
System.out.println(StaticInnerClass.age);
StaticInnerClass.go();
//在外部类中,访问静态内部类中的非静态属性和方法
//这时候需要先创建静态内部类对象,使用对象来访问
StaticInnerClass sic = new StaticInnerClass();
System.out.println(sic.name);
sic.run();
}
/*声明开始*/
public static class StaticInnerClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void test(String name){
//静态内部类访问自己的属性和方法
//静态和非静态的都可以访问
System.out.println(this.name );
System.out.println(StaticInnerClass.this.name);
System.out.println(StaticInnerClass.age);
StaticInnerClass.this.run() ;
StaticInnerClass.go();
//静态内部类中访问外部类中的静态属性和静态方法
//在静态内部类中访问不了外部类中的非静态属性和方法
//System.out.println(StaticOutterClass.this.name);【报错】
System.out.println(StaticOutterClass.age);
StaticOutterClass.go();
}
}
/*声明结束*/
}
在静态内部类中访问不了外部类的非静态属性和方法。
外部类中要访问静态内部类的静态属性和方法可以直接访问。
外部类要访问静态内部类的非静态属性和方法,要先创建内部类对象再用对象进行访问。
注意,静态内部类和成员内部类不同,静态内部类由于是静态的,它可以独立存在,并不会依赖于外部类的对象。而成员内部类就必须依赖于外部类对象,也就是必须先要创建出外部类对象,才能接下来创建成员内部类对象。
外部类中调用静态内部类【通过创建对象访问】
public class StaticOutterClassTest{
public static void main(String[] args){
StaticInnerClass sic = new StaticInnerClass();
sic.run();
}
}
3、局部内部类
【用的最少的内部类形式,一般写在方法里】
格式:
//局部内部类
public class LocalOutterClass{
public void test(){
/*局部内部类声明开始*/
class LocalInnerClass{
}
/*局部内部类声明结束*/
}
}
注意,局部内部类是定义在方法中的一种内部类,这种情况下,这个内部类的作用范围就比较小了,只能在方法中起作用,出了这个方法代码块,局部内部类就没有了。
编译后的class文件:
LocalOutter$LocalInnerClass.class
LocalOutterClass.class
定义属性、方法、构造器
//局部内部类
public class LocalOutterClass{
public void test(){
/*局部内部类声明开始*/
class LocalInnerClass1{
private String name;
private int age;
public void run(){}
}
/*局部内部类声明结束*/
}
}
注意,局部内部类中,【不能】编写静态的属性和方法,局部内部类中,可以编写构造器,如果有需要的话,如果不编写的话,那么默认使用无参的构造器。
内部类和外部类相互访问
public class LocalOutterClass{
private String name;
private static int age;
public void run(){}
public static void go(){}
public void say(final String myName){
final int a = 1;
/* 局部内部类 声明开始 */
class LocalInnerClass{
private String name;
private int age;
public void run(){}
public void test(String name){
//访问当前最近的name变量,也就是这个参数
System.out.println(name);
//访问say方法中的myName属性【final】
System.out.println(myName);
//也可以访问这个方法中的局部变量
//但是要求这个局部变量必须是final修饰的
//JDK1.8的时候,这个局部变量不是final修饰的也可以被局部内部类访问,
//但是访问之后,这个变量就自动变为final的了。
System.out.println(a);
//访问LocalInnerClass类中的属性name
System.out.println(this.name);
System.out.println(LocalInnerClass.this.name);
//访问LocalOutterClass类中的属性和方法(静态和非静态)
System.out.println(LocalOutterClass.this.name);
System.out.println(LocalOutterClass.age);
LocalOutterClass.this.run();
LocalOutterClass.go();
}
}
/* 局部内部类 声明结束 */
//由于局部内部类在方法中,所以也只能在这个方法中使用
LocalInnerClass lic = new LocalInnerClass();
System.out.println(lic.name);
System.out.println(lic.age);
lic.run();
}
}
在其他类中,怎么使用这个内部类创建对象。出了这个局部内部类所在的方法之后,其他任何地方都使用不到这个局部内部类
4、匿名内部类
【将来在程序中使用最多的内部类】
匿名内部类可以定义在方法中,也可以在类中属性赋值的时候进行使用。但是绝大多数都会是在方法中定义和使用。匿名内部类由于没有名字,所以在定义的形式比较特殊。匿名内部类必须依附在一个类或者接口上来进行定义,如果是依附在一个类上,那么这个匿名内部类就默认是这个类的子类。如果依附在一个接口上,匿名匿名内部类就默认是这个接口的实现类。匿名内部类不能先声明再使用。
格式:
在方法中定义匿名内部类
//匿名内部类
public class AnonymousOutterClass{
public void test(){
//虽然抽象类不能直接new对象,但是可以依附在
//抽象类上声明并创建出一个匿名内部类的对象。
//这个大括号就是匿名内部类的代码实现
//这个匿名内部类继承了Person父类型
//父类型的引用指向子类对象(匿名内部类对象)
Person p = new Person(){
public void run(){
System.out.println("依附类的匿名内部类的实现");
}
};
Action a = new Action(){
public void go(){
System.out.println("依附接口的匿名内部类的实现");
}
};
}
}
abstract class Person{
public abstract void run();
}
interface Action{
public void go();
}
在属性是的匿名内部类
//匿名内部类
public class AnonymousOutterClass{
//在给类中属性private Person person; 赋值的时候
//=号右边使用了匿名内部类对象,来给=号左边的引用person进行赋值
private Person person = new Person(){
public void run(){
System.out.println("在属性上的匿名内部类的实现");
}
};
}
abstract class Person{
public abstract void run();
}
编译后的class文件:
1、2、3分别代码中的三个匿名内部类
AnonymousOutterClass$1.class
AnonymousOutterClass$2.class
AnonymousOutterClass$3.class
AnonymousOutterClass.class
反编译查看
> javap AnonymousOutterClass$1.class
Compiled from "AnonymousOutterClass.java"
class com.zzb.day26.AnonymousOutterClass$1 extends com.zzb.day26.Person {
final com.zzb.day26.AnonymousOutterClass this$0;
com.zzb.day26.AnonymousOutterClass$1(com.zzb.day26.AnonymousOutterClass);
public void run();
}
> javap AnonymousOutterClass$2.class
Compiled from "AnonymousOutterClass.java"
class com.zzb.day26.AnonymousOutterClass$2 extends com.zzb.day26.Person {
final com.zzb.day26.AnonymousOutterClass this$0;
com.zzb.day26.AnonymousOutterClass$2(com.zzb.day26.AnonymousOutterClass);
public void run();
}
> javap AnonymousOutterClass$3.class
Compiled from "AnonymousOutterClass.java"
class com.zzb.day26.AnonymousOutterClass$3 implements com.zzb.day26.Action {
final com.zzb.day26.AnonymousOutterClass this$0;
com.zzb.day26.AnonymousOutterClass$3(com.zzb.day26.AnonymousOutterClass);
public void go();
}
定义属性、方法、构造器
匿名内部类和其他的内部类不太一样。其他的内部类都可以定义自己的属性、方法、构造器,然后将来将来需要的时候可以使用。匿名内部类,虽然可以定义属性和方法,但是定义之后无法调用到。匿名内部类,我们是没有办法编写构造器的,因为这个类没有名字,但是编译之后会自动生成构造器的,通过 javap 就可以看到这个构造器的声明。
总结:在匿名内部类中,一般情况,我们不会编写单独的属性、方法(如果有需要的话,可以编写),因为在外面不能能直接调用,我们也编写不了构造器,因为没有名字。我们在匿名内部类中,做的最多的事情,就是【重写】父类中的方法,或者【实现】接口中的抽象方法。
Action a = new Action(){
private String name;
public void run(){
}
//go方法是实现接口Action中的抽象方法
public void go(){
this.name = "tom";
this.run();
System.out.println("这里就是匿名内部类的实现");
}
};
//编译报错
a.run();
因为run方法是匿名内部类中独有的方法,变量a是Action类型的,Action中没有定义run方法,所以使用变量a无法调用到run方法把变量a进行类型转换,转为这个匿名内部类的类型就可以了,因为这个run方法就是这个匿名内部类中独有的。但是由于匿名内部类没有名字,所以我们也无法把变量a转为这个类型。那么也就是我们无法直接调用到这个独有的run方法。但是可以【间接】的调用到,例如可以在go方法中调用run方法,然后再使用变量a俩调用go方法,最后也可以让run方法执行。
总结:在你们内部类中,定义直接的独有的属性和方法,一般情况意义不大,除非是想在这匿名内部类中自己单独使用。因为外部是没有办法【直接】调用到的。
匿名内部类和外部类的相互访问:
匿名内部类在方法中定义的时候,其实就是一个没有名字的局部内部类,所以这时候它和外部类相互访问的情况和局部内部类是一样的。
public class AnonymousOutterClass{
private String name;
private static int age;
public void test(){
final int num = 1;
Person p = new Person(){
public void run(){
System.out.println(num);
System.out.println(AnonymousOutterClass.this.name);
System.out.println(AnonymousOutterClass.age);
}
};
p.run();
}
}
在其他类中,怎么使用这个内部类创建对象
匿名内部类大多定义在方法中,并且没有名字,所以其他地方都无法使用这个匿名内部类。但是我们可以使用return语句把这个匿名内部类的对象,返回出去,让别人使用。
public Person test(){
final int num = 1;
Person p = new Person(){
public void run(){
System.out.println(num);
System.out.println(AnonymousOutterClass.this.name);
System.out.println(AnonymousOutterClass.age);
}
};
return p;
}