本帖最后由 范雨龙 于 2018-7-21 20:29 编辑
内部类
一、为什么要使用内部类?
二、内部类基础
三、成员内部类
四、局部内部类
五、匿名内部类
六、静态内部类
七、访问控制符对成员内部类的意义,以及成员内部类相互之间的关系
内部类
一、为什么要使用内部类?
在《Think in java》中有这样一句话:
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的,可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口知识解决了部分问题,而内部类使得多重继承的解决方案变得更加完整
/**
* 内部类实现多重继承的方式
* @author erhu
*
*/
//外部类继承Demo1
public class InnerClass extends Demo1 {
//内部类继承Demo2
class InnerClass_ extends Demo2{
}
}
//具体类Demo1
class Demo1 {
}
//具体类Demo2
class Demo2 {
}
其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性(摘自《Think in java》):
内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。
在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类
创建内部类对象的时刻并不依赖外围类对象的创建。
1.这就话并不是说内部类对象的创建与外部类无关,只是创建内部类对象并不是一定要在外部对象创建时创建,而是可以在需要使用时在创建。
2.在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外部类对象上。但是,如果你创建的是嵌套类(静态内部类),那么他就不需要对外部类对象的引用。
内部类并没有令人迷惑的"is-a"关系,他就是一个独立的实体。 5.内部类提供了更好的封装,除了该外围类,其他类都不能访问
二、内部类基础
在这个部分主要介绍内部类如何使用外部类的属性和方法,以及使用.this与.new。
当我们在创建一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素。
/**
* 外部类
* @author erhu
*
*/
class OuterDemo {
//私有外部类的字段
private String name;
private String age;
//私有外部类的方法
private String getName() {
return this.name;
}
private String getAge() {
return this.age;
}
/**
* 内部类
* @author erhu
*
*/
class InnerDemo {
//内部类能自由的访问外部类的字段
public InnerDemo() {
name = "inner";
age = "18";
}
//内部类能自由的访问外部类的方法
public void printOuter() {
System.out.println("名字:"+getName()+",年龄:"+getAge());
}
}
}
public class Demo3 {
public static void main(String[] args) {
OuterDemo outer = new OuterDemo();
//外部类名.内部类名 外部对象引用.new
OuterDemo.InnerDemo inner = outer.new InnerDemo();
inner.printOuter();
}
}
在这个例子中,我们可以看到内部类InnerClass可以对外部类OuterClass的属性进行无缝的访问,尽管它是private修饰的。这是因为当我们在创建某个外部类的内部类对象时,此时内部类对象必定会捕获一个指向那个外围类对象的引用。
其实在这个例子中我们还可知:引用内部类我们需要指明这个对象的类型:OuterClass.InnerClass。同时如果我们需要创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类:OuterClass.InnerClass inner = outerClass.new InnerClass();
如果我们需要生成外部类对象的引用,可以使用OuterClass.this,这样就能够产生一个外部类对象的引用了。这点在编译器就知晓了,没有任何运行时的成本。
public class Demo4 {
public static void main(String[] args) {
//获得内部类对象引用
OuterDemo2.InnerDemo2 inner = new OuterDemo2().new InnerDemo2();
//获得外部类对象引用并调用其方法
inner.getOuter().print();
}
}
//外部类
class OuterDemo2 {
public void print() {
System.out.println("hello ,i'm outer");
}
//内部类
class InnerDemo2 {
//放回外部类对象的引用
public OuterDemo2 getOuter() {
return OuterDemo2.this;
}
}
}
//结果:hello ,i'm outer
到这里我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,他就与外部类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClas.class和OuterClass$InnerClass.class。
在java中内部类主要分为成员内部类,局部内部类,匿名内部类,静态内部类。
三、成员内部类
成员内部类也是最普通的内部类,它是外部类的一个成员,所以它是可以无限制的访问外部类的所有成员属性和方法,尽管是private的,但是外部类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
在成员内部类中要注意两点:
1. 成员内部类中不能存在任何static的变量和方法
2. 成员内部类是依附外部类的,所以只有先创建了外部类才能够创建内部类。
/**
* 外部类
* @author erhu
*
*/
public class OuterDemo3 {
//私有化字段
private String name;
//私有方法
private String getName() {
return this.name+"-.-";
}
//推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时
public InnerDemo3 getInnerDemo3() {
return new InnerDemo3();
}
/**
* 内部类
* @author erhu
*
*/
class InnerDemo3 {
//使用外部类字段
public InnerDemo3() {
name = "Outer";
}
//调用外部类方法
public void print() {
System.out.println(getName());
}
}
public static void main(String[] args) {
OuterDemo3 outer = new OuterDemo3();
outer.getInnerDemo3().print();
}
}
//结果:Outer-.-
推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时
四、局部内部类
有这样一种内部类,它是嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,他只能在该方法和属性中被使用,出了该方法和属性就会失效。
《Think in java》
//定义在方法里
public class Parcel5 {
public Destionation destionation(String str){
class PDestionation implements Destionation{
private String label;
private PDestionation(String whereTo){
label = whereTo;
}
public String readLabel(){
return label;
}
}
return new PDestionation(str);
}
public static void main(String[] args) {
Parcel5 parcel5 = new Parcel5();
Destionation d = parcel5.destionation("chenssy");
}
}
//定义在作用域里
public class Parcel6 {
private void internalTracking(boolean b){
if(b){
class TrackingSlip{
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip(){
return id;
}
}
TrackingSlip ts = new TrackingSlip("chenssy");
String string = ts.getSlip();
}
}
public void track(){
internalTracking(true);
}
public static void main(String[] args) {
Parcel6 parcel6 = new Parcel6();
parcel6.track();
}
}
五、匿名内部类
/**
* 匿名内部类实例
* @author erhu
*
*/
public class OuterDemo4 {
public Demo5 outer() {
//内部类对局部变量的引用,此时局部变量为final,
//原因是java对闭包支持的不完整,内部类中没有局部变量的引用
final String s1 = "Hello";
String s2 = "World";
//匿名内部类
return new Demo5() {
@Override
public void inner() {
System.out.println(s1);
}
};
}
public static void main(String[] args) {
OuterDemo4 outer = new OuterDemo4();
outer.outer().inner();
}
}
//用于实现匿名内部类的接口
interface Demo5 {
void inner();
}
//结果:Hello
匿名内部类是没有访问修饰符的
new匿名内部类,这个类(或接口)首先是要存在的。如果将Demo5接口注释掉,编译就会出错。
匿名内部类(局部类也有类似情况)对局部变量的引用时,局部变量必须为final,具体原因见另外一篇文章
匿名内部类是没有构造方法的。
*.匿名内部类待深入研究
六、静态内部类
关键字static可以修饰成员变量、方法、代码块,它还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,也可以成为嵌套内部类。
静态内部类与非静态内部类之间的最大的区别,非晶态内部类在编译后会隐含地保存外部类的引用,但是静态内部类却没有。没有这个引用就意味着:
1.它的创建是不需要依赖外部类的
2.它不能使用任何外部类的非static成员变量和方法
package erhu.inner;
/**
* 外部类实例
* @author erhu
*
*/
class OuterDemo5 {
private static String outerName = "outer";
private int outerAge = 18;
//外部静态方法和非静态方法都可以调用静态内部类的方法即成员,访问内部静态成员可以直接使用 类名.字段名(或方法)
public static void outer1() {
System.out.println("外部类可以直接调用静态内部类的静态成员及方法:"+Inner1.innerName);
Inner1.inner1();
System.out.println("外部类调用静态内部类的非静态成员,然后需要先实例内部类:"+new Inner1().innerAge);
}
//外部类非晶态方法调用静态内部类与外部静态方法一样
public void outer2() {
System.out.println("外部类可以直接调用静态内部类的静态成员及方法:"+Inner1.innerName);
Inner1.inner1();
System.out.println("外部类调用静态内部类的非静态成员,然后需要先实例内部类:"+new Inner1().innerAge);
}
/**
* 静态内部类class前加static
* @author erhu
*
*/
static class Inner1 {
//静态内部类可以定义自己的静态或非静态成员或方法,但是只能使用外部类的静态资源,因为没有外部对象的引用
static String innerName = "inner1";
String innerAge = "22";
public static void inner1() {
System.out.println("inner1(静态内部类):调用outer静态成员变量,name="+outerName);
}
}
/**
* 非静态内部类可以访问外部类的一切成员
* @author erhu
*
*/
class Inner2 {
//非晶态内部类不能使用static关键字,即内部不予许有自己的静态成员和方法
//但是可以调用外部类的任何成员以及方法,包括静态的
public void inner2() {
System.out.println("inner2(非静态内部类):调用outer静态成员和非静态成员,name="+outerName+",age="+outerAge);
}
}
}
/**
* 测试类
* @author erhu
*
*/
public class Demo6 {
public static void main(String[] args) {
OuterDemo5 outer = new OuterDemo5();
outer.outer2();
//可以直接使用内部类名.静态方法,但是要导包import erhu.inner.OuterDemo5.Inner1;
//同样可以直接使用OuterDemo5.Inner1.inner1();
//静态内部类可以直接实例化,不需要外部类的创建,但是外部类的字节码同样会加载
OuterDemo5.Inner1 Inner1 = new OuterDemo5.Inner1();
Inner1.inner1();
//其实同样可以采用导包的形式,但是写完整更易读
OuterDemo5.Inner2 inner2 = outer.new Inner2();
inner2.inner2();
}
}
/**
* 结果:
* 外部类可以直接调用静态内部类的静态成员及方法:inner1
inner1(静态内部类):调用outer静态成员变量,name=outer
外部类调用静态内部类的非静态成员,然后需要先实例内部类:22
inner1(静态内部类):调用outer静态成员变量,name=outer
inner2(非静态内部类):调用outer静态成员和非静态成员,name=outer,age=18
*/
小节:
静态内部类:
1. 静态内部类可以只能调用外部类的静态成员,因为没有外部类的引用
2. 静态类可以在外部类没有实例化的情况下直接实例化,但是外部类字节码依然会加载
3. 静态内部类可以有自己的非静态成员,外部类访问这些非静态成员就必须创建静态内部类对象,通过对象调用
非静态内部类:
1. 可以访问外部类的静态和非静态成员,因为拥有外部类对象的引用
2. 非静态内部类如要实例化,必须先拥有外部类对象
3. 非静态内部类不能拥有自己的静态成员
外部类: 外部类可以通过类名直接调用静态内部类的静态成员,非静态成员要通过内部类对象调用
七、访问控制符对成员内部类的意义,以及成员内部类相互之间的关系
事例:
/**
* 成员内部类与访问控制符之间的关系,以及成员内部类互相之间的关系
* 内部类可以被private,default,protected,public四种访问修饰符访问
* @author erhu
*
*/
public class Demo1 {
public static void main(String[] args) {
//外部类能访问私有内部类的私有成员
OuterDemo1 outer = new OuterDemo1();
outer.outer1();
/*私有内部类不能内外界访问,无法创建对象,更不用说访问成员了
* OuterDemo1.InnerDemo1 inner = outer.new InnerDemo1();出错
*/
/*
* 非私有内部类虽然能在外部创建对象,但是不能访问其私有对象
* inner2.inner3();出错
*/
OuterDemo1.InnerDemo2 inner2 = outer.new InnerDemo2();
}
}
class OuterDemo1 {
//外部类调用私有内部类的私有方法,
//成员内部类相当于外部类的成员,尽管是私有的但是还是可以访问
public void outer1() {
new InnerDemo1().inner1();
}
//私有内部类
private class InnerDemo1 {
//私有内部类的私有方法
private void inner1() {
System.out.println("this is inner1");
}
public void inner2() {
System.out.println("this is inner2");
}
}
//非私有内部类
class InnerDemo2 {
//内部类私有方法
private void inner3() {
//内部类能创建私有内部类的对象,并调用其私有成员,
//因为成员内部类相对与外部类的成员,所以成员之间可以互相访问
InnerDemo1 inner1 = new InnerDemo1();
inner1.inner1();
System.out.println("this is inner3");
}
}
//静态内部类
static class InnerDemo3 {
public void inner4() {
/*
* 静态内部内无法创建非静态内部类,因为没有外部类对象的引用,所以无法创建非静态内部类
* InnerDemo1 inner1 = new InnerDemo1();出错
*/
}
}
}
|
|