第一篇:Java内部类总结
内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类可为静态,可用protected和 private修饰(而外部类只能使用public和缺省的包访问权限)。内部类主要有以下几类:成员内部类、局部内部类、静态内部类、匿名内部类
为什么需要内部类?
典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外围类的对象。所以你可以认为内部类提供了某种进入其外围类的窗口。使用内部类最吸引人的原因是:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继 承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效 地实现了“多重继承”。
A:成员内部类
作为外部类的一个成员存在,与外部类的属性、方法并列。
public class Outer { private static int i = 1;private int j = 10;private int k = 20;
public static void outer_f1(){ }
public void outer_f2(){ }
// 成员内部类中,不能定义静态成员
// 成员内部类中,可以访问外部类的所有成员 class Inner { // static int inner_i = 100;//内部类中不允许定义静态变量 int j = 100;// 内部类和外部类的实例变量可以共存 intinner_i = 1;
void inner_f1(){ System.out.println(i);//在内部类中访问内部类自己的变量直接用变量名 System.out.println(j);//在内部类中访问内部类自己的变量也可以用this.变量名 System.out.println(this.j);//在内部类中访问外部类中与内部类同名的实例变量用外部类名.this.变量名 System.out.println(Outer.this.j);//如果内部类中没有与外部类同名的变量,则可以直接用变量名访问外部类变量 System.out.println(k);outer_f1();outer_f2();} }
//外部类的非静态方法访问成员内部类 public void outer_f3(){ Inner inner = new Inner();inner.inner_f1();}
// 外部类的静态方法访问成员内部类,与在外部类外部访问成员内部类一样 public static void outer_f4(){ //step1 建立外部类对象 Outer out = new Outer();//step2 根据外部类对象建立内部类对象 Inner inner = out.new Inner();//step3 访问内部类的方法 inner.inner_f1();}
publicstaticvoid main(String[] args){ //outer_f4();//该语句的输出结果和下面三条语句的输出结果一样
//如果要直接创建内部类的对象,不能想当然地认为只需加上外围类Outer的名字,//就可以按照通常的样子生成内部类的对象,而是必须使用此外围类的一个对象来 //创建其内部类的一个对象:
//Outer.Inner outin = out.new Inner()//因此,除非你已经有了外围类的一个对象,否则不可能生成内部类的对象。因为此 //内部类的对象会悄悄地链接到创建它的外围类的对象。如果你用的是静态的内部类,//那就不需要对其外围类对象的引用。Outer out = new Outer();Outer.Inner outin = out.new Inner();outin.inner_f1();} }
注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。
B:局部内部类
在方法中定义的内部类称为局部内部类。与局部变量类似,局部内部类不能有访问说明符,因为它不是外围类的一部分,但是它可以访问当前代码块内的常量,和此外围类所有的成员。
public class Outer { private ints = 100;private int out_i = 1;
public void f(final int k){ final int s = 200;int i = 1;final int j = 10;
//定义在方法内部 class Inner { ints = 300;// 可以定义与外部类同名的变量
// static int m = 20;//不可以定义静态变量 Inner(int k){ inner_f(k);}
intinner_i = 100;
void inner_f(int k){ //如果内部类没有与外部类同名的变量,在内部类中可以直接访问外部类的实例变量 System.out.println(out_i);//可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的 System.out.println(j);//System.out.println(i);//如果内部类中有与外部类同名的变量,直接用变量名访问的是内部类的变量 System.out.println(s);//用this.变量名访问的也是内部类变量 System.out.println(this.s);//用外部类名.this.内部类变量名访问的是外部类变量 System.out.println(Outer.this.s);} } new Inner(k);} publicstaticvoid main(String[] args){ // 访问局部内部类必须先有外部类对象 Outer out = new Outer();out.f(3);} }
C:静态内部类(嵌套类):(注意:前两种内部类与变量类似,所以可以对照参考变量)
如果你不需要内部类对象与其外围类对象之间有联系,那你可以将内部类声明为static。这通常称为嵌套类(nested class)。想要理解static应用于内部类时的含义,你就必须记住,普通的内部类对象隐含地保存了一个引用,指向创建它的外围类对象。然而,当内部 类是static的时,就不是这样了。嵌套类意味着:
1.要创建嵌套类的对象,并不需要其外围类的对象。
2.不能从嵌套类的对象中访问非静态的外围类对象。
publicclass Outer { privatestaticinti = 1;privateintj = 10;publicstaticvoidouter_f1(){ }
publicvoidouter_f2(){ }
// 静态内部类可以用public,protected,private修饰
// 静态内部类中可以定义静态或者非静态的成员 staticclass Inner { staticintinner_i = 100;intinner_j = 200;staticvoidinner_f1(){ //静态内部类只能访问外部类的静态成员(包括静态变量和静态方法)System.out.println(“Outer.i” + i);outer_f1();}
voidinner_f2(){ // 静态内部类不能访问外部类的非静态成员(包括非静态变量和非静态方法)// System.out.println(“Outer.i”+j);// outer_f2();} }
publicvoidouter_f3(){ // 外部类访问内部类的静态成员:内部类.静态成员 System.out.println(Inner.inner_i);Inner.inner_f1();// 外部类访问内部类的非静态成员:实例化内部类即可 Inner inner = new Inner();inner.inner_f2();}
publicstaticvoid main(String[] args){ newOuter().outer_f3();} }
生成一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类(正常情况下,你不能在接口内部放置任何代码,但嵌套类可以作为接口的一部 分,因为它是static 的。只是将嵌套类置于接口的命名空间内,这并不违反接口的规则)
D:匿名内部类(from thinking in java 3th)
简单地说:匿名内部类就是没有名字的内部类。什么情况下需要使用匿名内部类?如果满足下面的一些条件,使用匿名内部类是比较合适的:
·只用到类的一个实例。
·类在定义后马上用到。
·类非常小(SUN推荐是在4行代码以下)
·给类命名并不会导致你的代码更容易被理解。
在使用匿名内部类时,要记住以下几个原则:
·匿名内部类不能有构造方法。
·匿名内部类不能定义任何静态成员、方法和类。
·匿名内部类不能是public,protected,private,static。
·只能创建匿名内部类的一个实例。
·一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
·因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
下面的例子看起来有点奇怪:
//在方法中返回一个匿名内部类 public class Parcel6 { public Contents cont(){ return new Contents(){ private int i = 11;
public int value(){ return i;} };// 在这里需要一个分号 }
public static void main(String[] args){ Parcel6 p = new Parcel6();Contents c = p.cont();} }
cont()方法将下面两个动作合并在一起:返回值的生成,与表示这个返回值的类的定义!进一步说,这个类是匿名的,它没有名字。更糟的是,看起来是你正要创建一个Contents对象:
return new Contents()
但是,在到达语句结束的分号之前,你却说:“等一等,我想在这里插入一个类的定义”:
return new Contents(){ private int i = 11;public int value(){ return i;} };
这种奇怪的语法指的是:“创建一个继承自Contents的匿名类的对象。”通过new 表达式返回的引用被自动向上转型为对Contents的引用。匿名内部类的语法是下面例子的简略形式:
class MyContents implements Contents { private int i = 11;public int value(){ return i;} } return new MyContents();
在这个匿名内部类中,使用了缺省的构造器来生成Contents。下面的代码展示的是,如果你的基类需要一个有参数的构造器,应该怎么办:
public class Parcel7 { public Wrapping wrap(int x){ // Base constructor call: return new Wrapping(x){ // Pass constructor argument.public int value(){ return super.value()* 47;} };// Semicolon required } public static void main(String[] args){ Parcel7 p = new Parcel7();Wrapping w = p.wrap(10);} }
只需简单地传递合适的参数给基类的构造器即可,这里是将x 传进new Wrapping(x)。在匿名内部类末尾的分号,并不是用来标记此内部类结束(C++中是那样)。实际上,它标记的是表达式的结束,只不过这个表达式正 巧包含了内部类罢了。因此,这与别的地方使用的分号是一致的。
如果在匿名类中定义成员变量,你同样能够对其执行初始化操作:
public class Parcel8 { // Argument must be final to use inside // anonymous inner class: public Destination dest(final String dest){ return new Destination(){ private String label = dest;public String readLabel(){ return label;} };} public static void main(String[] args){ Parcel8 p = new Parcel8();Destination d = p.dest(“Tanzania”);} }
如果你有一个匿名内部类,它要使用一个在它的外部定义的对象,编译器会要求其参数引用是final 型的,就像dest()中的参数。如果你忘记了,会得到一个编译期错误信息。如果只是简单地给一个成员变量赋值,那么此例中的方法就可以了。但是,如果你 想做一些类似构造器的行为,该怎么办呢?在匿名类中不可能有已命名的构造器(因为它根本没名字!),但通过实例初始化,你就能够达到为匿名内部类“制作” 一个构造器的效果。像这样做:00000000000000
abstract class Base { public Base(int i){ System.out.println(“Base constructor, i = ” + i);} public abstract void f();}
public class AnonymousConstructor { public static Base getBase(int i){ return new Base(i){ { System.out.println(“Inside instance initializer”);} public void f(){ System.out.println(“In anonymous f()”);} };} public static void main(String[] args){ Base base = getBase(47);base.f();} }
在此例中,不要求变量i 一定是final 的。因为i 被传递给匿名类的基类的构造器,它并不会在匿名类内部被直接使用。下例是带实例初始化的“parcel”形式。注意dest()的参数必须是final,因为它们是在匿名类内被使用的。
public class Parcel9 { public Destinationdest(final String dest, final float price){ return new Destination(){ private int cost;// Instance initialization for each object: { cost = Math.round(price);if(cost > 100)System.out.println(“Over budget!”);}
private String label = dest;public String readLabel(){ return label;} };} public static void main(String[] args){ Parcel9 p = new Parcel9();Destination d = p.dest(“Tanzania”, 101.395F);} }
在实例初始化的部分,你可以看到有一段代码,那原本是不能作为成员变量初始化的一部分而执行的(就是if 语句)。所以对于匿名类而言,实例初始化的实际效果就是构造器。当然它受到了限制:你不能重载实例初始化,所以你只能有一个构造器。
从多层嵌套类中访问外部
一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员,如下所示:
class MNA { private void f(){} class A { private void g(){} public class B { void h(){ g();f();} } } } public class MultiNestingAccess { public static void main(String[] args){ MNA mna = new MNA();MNA.A mnaa = mna.new A();MNA.A.B mnaab = mnaa.new B();mnaab.h();} }
可以看到在MNA.A.B中,调用方法g()和f()不需要任何条件(即使它们被定义为private)。这个例子同时展示了如何从不同的类里面创建多层嵌套的内部类对象的基本语法。“.new”语法能产生正确的作用域,所以你不必在调用构造器时限定类名。
内部类的重载问题
如果你创建了一个内部类,然后继承其外围类并重新定义此内部类时,会发生什么呢?也就是说,内部类可以被重载吗?这看起来似乎是个很有用的点子,但是“重载”内部类就好像它是外围类的一个方法,其实并不起什么作用:
class Egg { private Yolk y;
protectedclass Yolk { public Yolk(){ System.out.println(“Egg.Yolk()”);} }
public Egg(){ System.out.println(“New Egg()”);y = new Yolk();} }
publicclass BigEgg extends Egg { publicclass Yolk { public Yolk(){ System.out.println(“BigEgg.Yolk()”);} }
publicstaticvoid main(String[] args){ new BigEgg();} }
输出结果为:
New Egg()Egg.Yolk()
缺省的构造器是编译器自动生成的,这里是调用基类的缺省构造器。你可能认为既然创建了BigEgg 的对象,那么所使用的应该是被“重载”过的Yolk,但你可以从输出中看到实际情况并不是这样的。
这个例子说明,当你继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化。这两个内部类是完全独立的两个实体,各自在自己的命名空间内。当然,明确地继承某个内部类也是可以的:
class Egg2 { protected class Yolk { public Yolk(){ System.out.println(“Egg2.Yolk()”);}
public void f(){ System.out.println(“Egg2.Yolk.f()”);} }
private Yolk y = new Yolk();
public Egg2(){ System.out.println(“New Egg2()”);}
public void insertYolk(Yolk yy){ y = yy;}
public void g(){ y.f();} }
public class BigEgg2 extends Egg2 { public class Yolk extends Egg2.Yolk { public Yolk(){ System.out.println(“BigEgg2.Yolk()”);}
public void f(){ System.out.println(“BigEgg2.Yolk.f()”);} }
public BigEgg2(){ insertYolk(new Yolk());}
public static void main(String[] args){ Egg2 e2 = new BigEgg2();e2.g();} }
输出结果为:
Egg2.Yolk()New Egg2()Egg2.Yolk()BigEgg2.Yolk()BigEgg2.Yolk.f()
现在BigEgg2.Yolk 通过extends Egg2.Yolk 明确地继承了此内部类,并且重载了其中的方法。Egg2 的insertYolk()方法使得BigEgg2 将它自己的Yolk 对象向上转型,然后传递给引用y。所以当g()调用y.f()时,重载后的新版的f()被执行。第二次调用Egg2.Yolk()是 BigEgg2.Yolk 的构造器调用了其基类的构造器。可以看到在调用g()的时候,新版的f()被调用了。
内部类的继承问题(thinking in java 3th p294)
因为内部类的构造器要用到其外围类对象的引用,所以在你继承一个内部类的时候,事情变得有点复杂。问题在于,那个“秘密的”外围类对象的引用必须被初始化,而在被继承的类中并不存在要联接的缺省对象。要解决这个问题,需使用专门的语法来明确说清它们之间的关联:
class WithInner { class Inner { Inner(){ System.out.println(“this is a constructor in WithInner.Inner”);};} }
public class InheritInner extends WithInner.Inner { //!InheritInner(){} // Won't compile InheritInner(WithInner wi){ wi.super();System.out.println(“this is a constructor in InheritInner”);}
public static void main(String[] args){ WithInner wi = new WithInner();InheritInner ii = new InheritInner(wi);} }
输出结果为:
this is a constructor in WithInner.Inner this is a constructor in InheritInner
可以看到,InheritInner 只继承自内部类,而不是外围类。但是当要生成一个构造器时,缺省的构造器并不算好,而且你不能只是传递一个指向外围类对象的引用。此外,你必须在构造器内使用如下语法:
enclosingClassReference.super();
这样才提供了必要的引用,然后程序才能编译通过。
1.实例化非静态内部类时是否需要先实例化外部类,为什么?
2.非静态内部类可以访问其外部类的哪些成员,为什么?
3.内部类可以定义在外部类的哪些位置,要实例化各位置的内部类有哪些限制? 4.匿名内部类和普通内部类有什么区别,为什么创建匿名内部类时可以new抽象类或接口?
5.如何在匿名内部类实现构造器的行为?
6.嵌套类(静态内部类)和普通非静态类有什么区别?
7.当继承一个类时,如何覆盖它的内部类?
回答:
1.实例化非静态内部类需要先实例化外部类,因为非静态内部类就像外部类的实例域一样,和外部类是有关联的。
2.非静态内部类可以访问其外部类的所有成员,因为在实例化内部类时,会隐式的保留其外部类的实例的一个引用,通过该引用来访问外部类的成员,而内部类是在外部类的里面,所以不受访问修饰符的限制。
3.内部类可以定义在外部类的内部、方法体、局部域,在内部的类可以通过外部类的实例来实例化,方法体和局部域 只能在定义的域实例化。4.匿名内部类没有类名,没有构造函数,当在匿名内部类里使用外部类定义的对象时,需要用final修改该对象,匿名
内部类只能单继承单实现。匿名内部类new的并非对象本身,而是作为其子类实例化,所以new抽象类或接口时,实际上是实例化该抽象类的一个子类或实现该接口的一个类。
5.通过实例域初始化(在内部类加一对花括号)来实现匿名内部类的构造器行为。
6.1)嵌套类不保留其外部类的实例的引用,因为嵌套类和外部没有任何关系。2)实例化嵌套类不需要先实例化其外部类,直接使用嵌套类的限定名进行实例化(如果嵌套类的访问级别允许)3)内部类不能包含静态域和嵌套类,嵌套类可以。
7.继承一个A类时,对于A类的内部类不会有什么影响变化,如果要覆盖A类的内部类,则可以定义一个内部类继承A类的内部类。
第二篇:JAVA 内部类的简单总结
核心提示:定义在一个类内部的类叫内部类,包含内部类的类称为外部类。内部类提供更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中其他类访问。内部类可以声明public、protected、private等访问限制,可以声明为abstract的供其他内部类或外部类继承与扩展定义在一个类内部的类叫内部类,包含内部类的类称为外部类。内部类提供更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中其他类访问。内部类可以声明public、protected、private等访问限制,可以声明为abstract的供其他内部类或外部类继承与扩展,或者声明为static、final的,也可以实现特定的接口。static的内部类行为上象一个独立的类,非static在行为上类似类的属性或方法且禁止声明static的方法。内部类可以访问外部类的所有方法与属性,内部类被单成其外部类的成员,同一个类的成员之间可以相互访问。但外部类不能访问内部类的实现细节,如内部类的属性。static的内部类只能访问外部类的静态属性与方法。匿名内部类适合用于创建那些仅需要一次使用的类。
内部类被作为成员内部类定义,而且不是局部内部类。成员内部类是一种与属性、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。成员内部类分为两种:静态内部类(使用static修饰)和非静态内部类,当在非静态内部类的方法访问某个变量时,系统优先在该方法内查找是否在该名字的局部变量,如果存在改名字的局部变量,就是用改变量;如果不存在则到内部类所在的外部类中查找是否存在改名字的属性,如果存在就是用。如果不存在编译错误。
如外部类属性、内部类属性与内部类里方法的局部变量通明,则通过使用this,外部类类名.this作为限制类区分。
1.public class DiscernVariable
2.{
3.private String prop = “外部类属性”;
4.private class InClass
5.{
6.private String prop = “内部类属性”;
7.public void info()
8.{
9.String prop = “局部变量”;
10.//通过 外部类类名.this.varName 访问外部类实例属性
11.System.out.println(“外部类的属性值:” + DiscernVariable.this.prop);
12.//通过 this.varName 访问外内部类实例的属性
13.System.out.println(“内部类的属性值:” + this.prop);
14.//直接访问局部变量
15.System.out.println(“局部变量的属性值:” + prop);
16.}
17.}
18.public void test()
19.{
20.InClass in = new InClass();
21.in.info();
22.}
23.public static void main(String[] args)
24.{
25.new DiscernVariable().test();
26.}
27.}
非静态内部类的成员可以访问外部类的private成员,但反过来不可以。非静态内部类成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问非静态内部类成员,则必须相示创建非静态内部类对象来调用访问其实例成员。
1.public class Outer
2.{
3.private int outProp = 9;
4.class Inner
5.{
6.private int inProp = 5;
7.public void acessOuterProp()
8.{
9.//内部类可以直接访问外部类的成员
10.System.out.println(“外部类的outProp属性值:” + outProp);
11.}
12.}
13.public void accessInnerProp()
14.{
15.//外部类不能直接访问内部类属性,下面代码出现编译错误
16.//System.out.println(“内部类的inProp属性值:” + inProp);
17.//如需访问内部类成员,必须显式创建内部类对象
18.System.out.println(“内部类的inProp属性值:” + new Inner().inProp);19.20.}
21.public static void main(String[] args)
22.{
23.//执行下面代码,只创建了外部类对象,还未创建内部类对象
24.Outer out = new Outer();
25.}
26.}
外部类按常规的类访问方式使用内部类,唯一的差别是外部类可以访问内部类的所有方法与属性,包括私有方法与属性。如:
1.pinner p = new pinner();
2.p.index = 20;
3.p.Print();
4.----这种方式适合外部类的非static方法;
5.6.pouter po = new pouter();
7.pinner pi = po.new pinner();
8.pi.index = 40;
9.pi.Print();
10.----这种方式适合外部类的static方法;
内部类类似外部类的属性,因此访问内部类对象时总是需要一个创建好的外部类对象。内部类对象通过‘外部类名.this.xxx’的形式访问外部类的属性与方法。如:
1.System.out.println(“Print in inner Outer.index=” + pouter.this.index);
2.System.out.println(“Print in inner Inner.index=” + this.index);
如果需要在其他类中访问内部类,可以使用:
(1)外部类提供创建内部类的方法供其他类使用。如:
1.// 外部类
2.pinner getInner()
3.{
4.return new pinner();
5.}
6.7.// 其他类
8.pouter.pinner pi = po.getInner();
9.pi.Print();
(2)直接创建内部类的对象。如:
1.pouter po = new pouter();
2.pouter.pinner pi = po.new pinner();
3.pi.Print();
内部类可以声明在外部类的方法中或语句块中。如果内部类需要访问包含它的外部类方法或语句块的局部变量或参数,则该局部变量或参数必须是final的。外部类的其他方法、其他类无法访问声明在方法内部或块内部的内部类。
如果一个类继承内部类,则创建该类的对象时需提供一个外部类的对象作为构造方法的参数。如:
1.class Car
2.{
3.class Wheel
4.{
5.6.}
7.}
8.9.class SuperWheel extends Car.Wheel
10.{
11.SuperWheel(Car car)
12.{
13.car.super();
14.}
15.16.public static void main(String [] args)
17.{
18.Car car = new Car();
19.SuperWheel wl = new SuperWheel(car);
20.}
21.}
如果创建命名的内部类没有多少实际意义时,可以创建匿名的内部类。比如使用内部类实现接口的功能(如事件处理器、适配器等),而功能的差异较大,需要根据实际的情况创建相应的内部类时,可以使用匿名内部类。简单的示例如下:
1.interface WebView
2.{
3.void doGet();
4.}
5.6.class A
7.{
8.WebView ShowName()
9.{
10.return new WebView()
11.{
12.void doGet()
13.{
14.System.out.println(“Name”);
15.}
16.};
17.}
18.19.WebView ShowCode()
20.{
21.return new WebView()
22.{
23.void doGet()
24.{
25.System.out.println(“Code”);
26.}
27.};
28.}
29.}
最后,JAVA 内部类还有一个作用,那就是实现JAVA的多继承。JAVA本身是不允许多继承的,如果我们想一个类继承多个基类,就可以使用内部类。通过内部类分别继承一个基类,外部类创建内部类的对象,并使用内部类的方法,变相地实现了多继承。
第三篇:java_内部类总结
内部类
内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权限)。内部类主要有以下几类:成员内部类、局部内部类、静态内部类、匿名内部类
为什么需要内部类? 典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外围类的对象。所以你可以认为内部类提供了某种进入其外围类的窗口。使用内部类最吸引人的原因是:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
A:成员内部类
作为外部类的一个成员存在,与外部类的属性、方法并列。
publicclass Outer { privatestaticinti = 1;privateintj = 10;privateintk = 20;
publicstaticvoidouter_f1(){ }
publicvoidouter_f2(){ }
// 成员内部类中,不能定义静态成员
// 成员内部类中,可以访问外部类的所有成员
class Inner { // static int inner_i = 100;//内部类中不允许定义静态变量
intj = 100;// 内部类和外部类的实例变量可以共存
intinner_i = 1;
void inner_f1(){ System.out.println(i);
//在内部类中访问内部类自己的变量直接用变量名
System.out.println(j);
//在内部类中访问内部类自己的变量也可以用this.变量名
System.out.println(this.j);
//在内部类中访问外部类中与内部类同名的实例变量用外部类名.this.变量名
System.out.println(Outer.this.j);
//如果内部类中没有与外部类同名的变量,则可以直接用变量名访问外部类变量
System.out.println(k);outer_f1();outer_f2();} }
//外部类的非静态方法访问成员内部类
publicvoidouter_f3(){ Inner inner = new Inner();inner.inner_f1();}
// 外部类的静态方法访问成员内部类,与在外部类外部访问成员内部类一样
publicstaticvoidouter_f4(){ //step1 建立外部类对象
Outer out = new Outer();//step2 根据外部类对象建立内部类对象
Inner inner = out.new Inner();//step3 访问内部类的方法
inner.inner_f1();}
publicstaticvoid main(String[] args){ //outer_f4();//该语句的输出结果和下面三条语句的输出结果一样
//如果要直接创建内部类的对象,不能想当然地认为只需加上外围类Outer的名字,//就可以按照通常的样子生成内部类的对象,而是必须使用此外围类的一个对象来
//创建其内部类的一个对象:
//Outer.Inner outin = out.new Inner()
//因此,除非你已经有了外围类的一个对象,否则不可能生成内部类的对象。因为此
//内部类的对象会悄悄地链接到创建它的外围类的对象。如果你用的是静态的内部类,//那就不需要对其外围类对象的引用。
Outer out = new Outer();Outer.Inner outin = out.new Inner();outin.inner_f1();} }
注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。
B:局部内部类
在方法中定义的内部类称为局部内部类。与局部变量类似,局部内部类不能有访问说明符,因为它不是外围类的一部分,但是它可以访问当前代码块内的常量,和此外围类所有的成员。
publicclass Outer { privateints = 100;privateintout_i = 1;
publicvoid f(finalint k){ finalint s = 200;int i = 1;finalint j = 10;
//定义在方法内部
class Inner { ints = 300;// 可以定义与外部类同名的变量
// static int m = 20;//不可以定义静态变量
Inner(int k){ inner_f(k);}
intinner_i = 100;
voidinner_f(int k){
//如果内部类没有与外部类同名的变量,在内部类中可以直接访问外部类的实例变量
System.out.println(out_i);
//可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的System.out.println(j);//System.out.println(i);
//如果内部类中有与外部类同名的变量,直接用变量名访问的是内部类的变量
System.out.println(s);
//用this.变量名访问的也是内部类变量
System.out.println(this.s);
//用外部类名.this.内部类变量名访问的是外部类变量
System.out.println(Outer.this.s);} } new Inner(k);}
publicstaticvoid main(String[] args){ // 访问局部内部类必须先有外部类对象
Outer out = new Outer();out.f(3);} }
C:静态内部类(嵌套类):(注意:前两种内部类与变量类似,所以可以对照参考变量)如果你不需要内部类对象与其外围类对象之间有联系,那你可以将内部类声明为static。这通常称为嵌套类(nested class)。想要理解static应用于内部类时的含义,你就必须记住,普通的内部类对象隐含地保存了一个引用,指向创建它的外围类对象。然而,当内部类是static的时,就不是这样了。嵌套类意味着:
1.要创建嵌套类的对象,并不需要其外围类的对象。2.不能从嵌套类的对象中访问非静态的外围类对象。
publicclass Outer { privatestaticinti = 1;privateintj = 10;publicstaticvoidouter_f1(){ }
publicvoidouter_f2(){ }
// 静态内部类可以用public,protected,private修饰
// 静态内部类中可以定义静态或者非静态的成员
staticclass Inner { staticintinner_i = 100;intinner_j = 200;staticvoidinner_f1(){ //静态内部类只能访问外部类的静态成员(包括静态变量和静态方法)
System.out.println(“Outer.i” + i);outer_f1();}
voidinner_f2(){ // 静态内部类不能访问外部类的非静态成员(包括非静态变量和非静态方法)
// System.out.println(“Outer.i”+j);
// outer_f2();
} }
publicvoidouter_f3(){ // 外部类访问内部类的静态成员:内部类.静态成员
System.out.println(Inner.inner_i);Inner.inner_f1();// 外部类访问内部类的非静态成员:实例化内部类即可
Inner inner = new Inner();inner.inner_f2();}
publicstaticvoid main(String[] args){ newOuter().outer_f3();} }
生成一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类(正常情况下,你不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分,因为它是static 的。只是将嵌套类置于接口的命名空间内,这并不违反接口的规则)
D:匿名内部类(from thinking in java 3th)
简单地说:匿名内部类就是没有名字的内部类。什么情况下需要使用匿名内部类?如果满足下面的一些条件,使用匿名内部类是比较合适的:
·只用到类的一个实例。
·类在定义后马上用到。
·类非常小(SUN推荐是在4行代码以下)
·给类命名并不会导致你的代码更容易被理解。
在使用匿名内部类时,要记住以下几个原则:
·匿名内部类不能有构造方法。
·匿名内部类不能定义任何静态成员、方法和类。
·匿名内部类不能是public,protected,private,static。
·只能创建匿名内部类的一个实例。
·一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
·因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
下面的例子看起来有点奇怪:
//在方法中返回一个匿名内部类 public class Parcel6 { public Contents cont(){ return new Contents(){ private int i = 11;public int value(){
return i;
}
};// 在这里需要一个分号
}
public static void main(String[] args){ Parcel6 p = new Parcel6();Contents c = p.cont();} } cont()方法将下面两个动作合并在一起:返回值的生成,与表示这个返回值的类的定义!进一步说,这个类是匿名的,它没有名字。更糟的是,看起来是你正要创建一个Contents对象:
return new Contents()但是,在到达语句结束的分号之前,你却说:“等一等,我想在这里插入一个类的定义”:
return new Contents(){ private int i = 11;public int value(){ return i;} };这种奇怪的语法指的是:“创建一个继承自Contents的匿名类的对象。”通过new 表达式返回的引用被自动向上转型为对Contents的引用。匿名内部类的语法是下面例子的简略形式:
class MyContents implements Contents { private int i = 11;public int value(){ return i;} } return new MyContents();在这个匿名内部类中,使用了缺省的构造器来生成Contents。下面的代码展示的是,如果你的基类需要一个有参数的构造器,应该怎么办:
public class Parcel7 { public Wrapping wrap(int x){ // Base constructor call: return new Wrapping(x){ // Pass constructor argument.public int value(){
return super.value()* 47;
}
};// Semicolon required } public static void main(String[] args){ Parcel7 p = new Parcel7();Wrapping w = p.wrap(10);} } 只需简单地传递合适的参数给基类的构造器即可,这里是将x 传进new Wrapping(x)。在匿名内部类末尾的分号,并不是用来标记此内部类结束(C++中是那样)。实际上,它标记的是表达式的结束,只不过这个表达式正巧包含了内部类罢了。因此,这与别的地方使用的分号是一致的。
如果在匿名类中定义成员变量,你同样能够对其执行初始化操作:
public class Parcel8 { // Argument must be final to use inside // anonymous inner class: public Destination dest(final String dest){ return new Destination(){ private String label = dest;public String readLabel(){ return label;} };} public static void main(String[] args){ Parcel8 p = new Parcel8();Destination d = p.dest(“Tanzania”);} }
如果你有一个匿名内部类,它要使用一个在它的外部定义的对象,编译器会要求其参数引用是final 型的,就像dest()中的参数。如果你忘记了,会得到一个编译期错误信息。如果只是简单地给一个成员变量赋值,那么此例中的方法就可以了。但是,如果你想做一些类似构造器的行为,该怎么办呢?在匿名类中不可能有已命名的构造器(因为它根本没名字!),但通过实例初始化,你就能够达到为匿名内部类“制作”一个构造器的效果。像这样做:
abstract class Base { public Base(int i){ System.out.println(“Base constructor, i = ” + i);} public abstract void f();}
public class AnonymousConstructor { public static Base getBase(int i){ return new Base(i){
{
System.out.println(“Inside instance initializer”);
}
public void f(){
System.out.println(“In anonymous f()”);
}
};} public static void main(String[] args){ Base base = getBase(47);base.f();} }
在此例中,不要求变量i 一定是final 的。因为i 被传递给匿名类的基类的构造器,它并不会在匿名类内部被直接使用。下例是带实例初始化的“parcel”形式。注意dest()的参数必须是final,因为它们是在匿名类内被使用的。
public class Parcel9 { public Destinationdest(final String dest, final float price){ return new Destination(){
private int cost;
// Instance initialization for each object:
{
cost = Math.round(price);if(cost > 100)
System.out.println(“Over budget!”);
}
private String label = dest;
public String readLabel(){ return label;} };
} public static void main(String[] args){ Parcel9 p = new Parcel9();Destination d = p.dest(“Tanzania”, 101.395F);} } 在实例初始化的部分,你可以看到有一段代码,那原本是不能作为成员变量初始化的一部分而执行的(就是if 语句)。所以对于匿名类而言,实例初始化的实际效果就是构造器。当然它受到了限制:你不能重载实例初始化,所以你只能有一个构造器。
从多层嵌套类中访问外部
一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员,如下所示:
class MNA { private void f(){} class A { private void g(){} public class B { void h(){ g();f();} } } } public class MultiNestingAccess { public static void main(String[] args){ MNA mna = new MNA();MNA.A mnaa = mna.new A();MNA.A.B mnaab = mnaa.new B();mnaab.h();} } 可以看到在MNA.A.B中,调用方法g()和f()不需要任何条件(即使它们被定义为private)。这个例子同时展示了如何从不同的类里面创建多层嵌套的内部类对象的基本语法。“.new”语法能产生正确的作用域,所以你不必在调用构造器时限定类名。
内部类的重载问题
如果你创建了一个内部类,然后继承其外围类并重新定义此内部类时,会发生什么呢?也就是说,内部类可以被重载吗?这看起来似乎是个很有用的点子,但是“重载”内部类就好像它是外围类的一个方法,其实并不起什么作用:
class Egg { private Yolk y;
protectedclass Yolk { public Yolk(){ System.out.println(“Egg.Yolk()”);} }
public Egg(){ System.out.println(“New Egg()”);y = new Yolk();} }
publicclass BigEgg extends Egg { publicclass Yolk { public Yolk(){ System.out.println(“BigEgg.Yolk()”);} }
publicstaticvoid main(String[] args){ new BigEgg();} }
输出结果为: New Egg()Egg.Yolk()
缺省的构造器是编译器自动生成的,这里是调用基类的缺省构造器。你可能认为既然创建了BigEgg 的对象,那么所使用的应该是被“重载”过的Yolk,但你可以从输出中看到实际情况并不是这样的。这个例子说明,当你继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化。这两个内部类是完全独立的两个实体,各自在自己的命名空间内。当然,明确地继承某个内部类也是可以的:
class Egg2 { protected class Yolk { public Yolk(){ System.out.println(“Egg2.Yolk()”);}
public void f(){ System.out.println(“Egg2.Yolk.f()”);} }
private Yolk y = new Yolk();
public Egg2(){ System.out.println(“New Egg2()”);}
public void insertYolk(Yolk yy){ y = yy;}
public void g(){ y.f();} }
public class BigEgg2 extends Egg2 { public class Yolk extends Egg2.Yolk { public Yolk(){ System.out.println(“BigEgg2.Yolk()”);}
public void f(){ System.out.println(“BigEgg2.Yolk.f()”);} }
public BigEgg2(){ insertYolk(new Yolk());}
public static void main(String[] args){ Egg2 e2 = new BigEgg2();e2.g();} }
输出结果为: Egg2.Yolk()New Egg2()Egg2.Yolk()BigEgg2.Yolk()BigEgg2.Yolk.f()
现在BigEgg2.Yolk 通过extends Egg2.Yolk 明确地继承了此内部类,并且重载了其中的方法。Egg2 的insertYolk()方法使得BigEgg2 将它自己的Yolk 对象向上转型,然后传递给引用y。所以当g()调用y.f()时,重载后的新版的f()被执行。第二次调用Egg2.Yolk()是BigEgg2.Yolk 的构造器调用了其基类的构造器。可以看到在调用g()的时候,新版的f()被调用了。
内部类的继承问题(thinking in java 3th p294)
因为内部类的构造器要用到其外围类对象的引用,所以在你继承一个内部类的时候,事情变得有点复杂。问题在于,那个“秘密的”外围类对象的引用必须被初始化,而在被继承的类中并不存在要联接的缺省对象。要解决这个问题,需使用专门的语法来明确说清它们之间的关联:
class WithInner {
class Inner { Inner(){ System.out.println(“this is a constructor in WithInner.Inner”);};} }
public class InheritInner extends WithInner.Inner {
//!InheritInner(){} // Won't compile InheritInner(WithInner wi){ wi.super();System.out.println(“this is a constructor in InheritInner”);}
public static void main(String[] args){ WithInner wi = new WithInner();InheritInner ii = new InheritInner(wi);} }
输出结果为:
this is a constructor in WithInner.Inner this is a constructor in InheritInner
可以看到,InheritInner 只继承自内部类,而不是外围类。但是当要生成一个构造器时,缺省的构造器并不算好,而且你不能只是传递一个指向外围类对象的引用。此外,你必须在构造器内使用如下语法:
enclosingClassReference.super();这样才提供了必要的引用,然后程序才能编译通过。
第四篇:内部类的问题相关(最终版)
内部类的问题相关
Java内部类的使用小结
内部类是指在一个外部类的内部再定义一个类。类名不需要和文件夹相同。
*内部类可以是静态static的,也可用public,default,protected和private修饰。(而外部顶级类即类名和文件名相同的只能使用public和default)。
注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。
1.成员内部类
成员内部类,就是作为外部类的成员,可以直接使用外部类的所有成员和方法,即使是private的。同时外部类要访问内部类的所有成员变量/方法,则需要通过内部类的对象来获取。
要注意的是,成员内部类不能含有static的变量和方法。因为成员内部类需要先创建了外部类,才能创建它自己的,了解这一点,就可以明白更多事情,在此省略更多的细节了。在成员内部类要引用外部类对象时,使用outer.this来表示外部类对象;而需要创建内部类对象,可以使用outer.innerobj = outerobj.new inner();public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.print(“Outer.new”);
inner = outer.getInner();
inner.print(“Outer.get”);
}
// 个人推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时public Inner getInner(){
return new Inner();
}
public class Inner {
public void print(String str){
System.out.println(str);
}
}
}
2.局部内部类
局部内部类,是指内部类定义在方法和作用域内。Thinking in Java给了这么两个例子: 定义在方法内:
public class Parcel4 {
public Destination destination(String s){
class PDestination implements Destination {
private String label;
private PDestination(String whereTo){
label = whereTo;
}
public String readLabel(){
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args){
Parcel4 p = new Parcel4();
Destination d = p.destination(“Tasmania”);
}
}
定义在作用域里:
public class Parcel5 {
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(“slip”);
String s = ts.getSlip();
}
}
public void track(){
internalTracking(true);
}
public static void main(String[] args){
Parcel5 p = new Parcel5();
p.track();
}
}
局部内部类也像别的类一样进行编译,但只是作用域不同而已,只在该方法或条件的作用域内才能使用,退出这些作用域后无法引用的。
3.嵌套内部类
嵌套内部类,就是修饰为static的内部类。声明为static的内部类,不需要内部类对象和外部类对象之间的联系,就是说我们可以直接引用outer.inner,即不需要创建外部类,也不需要创建内部类。
嵌套类和普通的内部类还有一个区别:普通内部类不能有static数据和static属性,也不能包含嵌套类,但嵌套类可以。而嵌套类不能声明为private,一般声明为public,方便调用。
4.匿名内部类
有时候我为了免去给内部类命名,便倾向于使用匿名内部类,因为它没有名字。例如:((Button)findViewById(R.id.start)).setOnClickListener(new Button.OnClickListener(){@Override
public void onClick(View v){
new Thread(){
@Override
public void run(){
// TODO Auto-generated method stub
}
}.start();
}
});
匿名内部类是不能加访问修饰符的。要注意的是,new 匿名类,这个类是要先定义的,看下面例子:
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Inner inner = outer.getInner(“Inner”, “gz”);
System.out.println(inner.getName());
}
public Inner getInner(final String name, String city){
return new Inner(){
private String nameStr = name;
public String getName(){
return nameStr;
}
};
}
}
//注释后,编译时提示类Inner找不到
/* interface Inner {
String getName();
} */
同时在这个例子,留意外部类的方法的形参,当所在的方法的形参需要被内部类里面使用时,该形参必须为final。这里可以看到形参name已经定义为final了,而形参city 没有被使用则不用定义为final。为什么要定义为final呢?在网上找到本人比较如同的解释: “这是一个编译器设计的问题,如果你了解java的编译原理的话很容易理解。
首先,内部类被编译的时候会生成一个单独的内部类的.class文件,这个文件并不与外部类在同一class文件中。
当外部类传的参数被内部类调用时,从java程序的角度来看是直接的调用例如:public void dosome(final String a,final int b){
class Dosome{public void dosome(){System.out.println(a+b)}};
Dosome some=new Dosome();
some.dosome();
}
从代码来看好像是那个内部类直接调用的a参数和b参数,但是实际上不是,在java编译器编译以后实际的操作代码是
class Outer$Dosome{
public Dosome(final String a,final int b){
this.Dosome$a=a;
this.Dosome$b=b;
}
public void dosome(){
System.out.println(this.Dosome$a+this.Dosome$b);
}
}}
从以上代码看来,内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数。
这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。”
(简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变)
因为匿名内部类,没名字,是用默认的构造函数的,无参数的,那如果需要参数呢?则需要该类有带参数的构造函数:
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Inner inner = outer.getInner(“Inner”, “gz”);
System.out.println(inner.getName());
}
public Inner getInner(final String name, String city){
return new Inner(name, city){
private String nameStr = name;
public String getName(){
return nameStr;
}
};
}
}
abstract class Inner {
Inner(String name, String city){
System.out.println(city);
}
abstract String getName();
}
注意这里的形参city,由于它没有被匿名内部类直接使用,而是被抽象类Inner的构造函数所使用,所以不必定义为final。
而匿名内部类通过实例初始化,可以达到类似构造器的效果:
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Inner inner = outer.getInner(“Inner”, “gz”);
System.out.println(inner.getName());
System.out.println(inner.getProvince());
}
public Inner getInner(final String name, final String city){
return new Inner(){
private String nameStr = name;
private String province;
// 实例初始化
{
if(city.equals(“gz”)){
province = “gd”;
}else {
province = "";
}
}
public String getName(){
return nameStr;
}
public String getProvince(){
return province;
}
};
}
}
interface Inner {
String getName();
String getProvince();
}
5.内部类的继承
内部类的继承,是指内部类被继承,普通类 extents 内部类。而这时候代码上要有点特
别处理。
至于内部类的重载,感觉Thinking in Java的例子很复杂,在平常应用中应该很少,因为有点难懂,不清晰。而内部类和闭包之间的事情,暂时放下,以后再看。
第五篇:部类如何造句
【注音】: bu lei
【意思】:概括性较大的类。
部类造句:
1、马克思的两部类分析模式,对现实经济仍然具有指导作用。
2、这里所说的是实现细节了,我们可以直接访问外部类中的变量,就好像这些变量定义在内部类中一样。
3、技术进步、增加工资、对外贸易都减缓两部类生产不均衡,延缓经济危机发生。
4、一旦定义了这些访问限制,Eclipse这个Java访问工具就会对内部类的访问提供和其他编译器警告一样的提示。
5、史部成为独立的部类经过了一个很长的发展过程。
6、安全科学是介于数学、自然科学、系统科学与哲学、社会科学、思维科学各科学部类之间的新兴交叉学科门类。
7、要对城市休闲产业集聚进行实证性研究,就必须在对休闲产业部类划分的前提下进行。
8、本文的目的在于改进连续杆战斗部类导弹杀伤概率的计算模型。
9、实际上,成员完全定义在类中的要求限制了局部类成员函数的复杂性。
10、如果把不同部类的增长情况进行分类的话,大部分出口价格从2001年以来的增长都可以归咎于燃料。
11、外围函数对局部类的私有成员没有特殊访问权,当然,局部类可以将外围函数设为友元。
12、我们拥有着自己的爱人和部类;拥有我们曾经吞咽过的美味;
13、但我们死的好富有。我们拥有着自己的爱人和部类;拥有我们曾经吞咽过的美味;
14、我们拥有着自己的爱人和部类;拥有我们曾经吞咽过的美味;拥有我们进入的身躯,我们在其中就像在河里游啊游。