一、成员内部类
1.例题分析:编写一个程序,创建一个类,并在类中创建一个成员内部类,通过成员内部类计算1到任意数的和的操作,并在外部类中进行测试。
我的答案:
package com.XueFeng.NithPointSix;
import java.util.Scanner;
public class TestMemberClass {
//定义成员内部类
class memberClass {
int toInteger; //计算1~ToInteger范围内的数字
memberClass(int toInteger) { //定义构造方法,用于初始化成员变量ToIntegger
this.toInteger=toInteger;
}
//求1~toInteger之间的和
public int summation() {
int sum=0;
for(int i=1;i<=toInteger;i++) {
sum+=i;
}
return sum;
}
}
//利用非静态成员方法对内部类实例化
public memberClass instance(int toInteger) {
return new memberClass(toInteger);
}
//定义main方法
public static void main(String[] args) {
int toInteger;
Scanner scan=new Scanner(System.in);
System.out.print("输入一个数字:");
toInteger=scan.nextInt();
TestMemberClass testMemberClass=new TestMemberClass();
TestMemberClass.memberClass innerClass=testMemberClass.instance(toInteger);
System.out.println("1~"+toInteger+"的和为:"+innerClass.summation());
}
}
代码执行内存分析:
参考答案
public class Example {
private class InExample{
//此处基本相同
int sum=0;
public int getSum(int a){
for (int i=1;i<=a;i++){
sum=sum+i;
}
return sum;
}
}
//外部类的非静态成员方法
public void printSum(int x){
InExample in=new InExample(); //实例化内部类对象
int s=in.getSum(x); //调用内部类成员方法
System.out.println("1~"+x+"的和是:"+s);
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Example ex=new Example(); //实例化外部类对象
ex.printSum(10); //调用外部类的成员方法
}
}
参考答案和我的答案相比,原理相同,都是通过在main方法中调用非静态方法对内部类进行实例化。我的代码将范围x写成成员变量,把sum写成局部变量,和参考答案相反。不过,都差不多吧。
成员内部类可以任意使用其外部类的任何成员(包括成员方法和成员变量),即使该成员以private进行修饰,这就好比在类的成员方法中使用类的成员变量一样。而相反,外部类不能够直接使用内部类的成员方法和成员变量,需要使用“内部类对象的引用.成员变量或方法”。如果要对内部类进行实例化,必须要在外部类或者非静态成员方法中进行实例化,例如在main方法中如此使用,将是错误的:
public static void main(String[] args) {
OuterClass.innerClass in=new innerClass();
}
但可以这样使用:
public static void main(String[] args){
OuterClass out=new OuterClass();
OuterClass.innerClass in=out.new innerClass();
}
还可以调用非静态类进行实例化:
class OuterClass {
class innerClass{
........
}
public innerClass instance(){
return new innerClass();
}
public static void main(String[] args){
OuterClass out=new OuterClass();
OuterClass.innerClass in=out.instance();
}
}
有或者不在成员方法中实例化:
public OutClass {
innerClass in=new innerClass();
class innerClass(){
....
}
}
虽然书中说“在《外部类和非静态方法(即有static修饰的)》之外实例化内部类对象,要使用如”外部类.内部类 in=外部类对象.外部类方法”来指定该对象的类型,不过尝试去掉“外部类”也不会发生错误,但是还是遵照书上的说法,毕竟这可能潜在内部的错误。
另一点值得注意的是,当外部类与内部类存在相同的变量时,我们应该如何区分它们呢?可以这样做:
public OutClass {
int member;
class innerClass {
int member;
public void method(){
this.member; //访问内部类的成员变量member
OutClass.this.member; //访问外部类的成员变量member
}
}
}
因为method()是内部类innerClass的成员方法,所以使用内部类对象的引用.成员方法,this自然指向innerClass,所以this.member指向的是内部类的成员变量。
对于成员内部类的实例化,我还要补充的一点是:内部类的实例化必须依靠外部类,只有当外部类实例化出一个外部类对象的引用时,才有可能存在内部类的实例化对象。
二、局部内部类
例题分析:编写java程序,创建一个类,在该类中定义一个方法,并在方法内部创建一个局部内部类,通过局部内部类求任意两个数值的乘积,并在外部类中进行测试。
package com.XueFeng.NithPointSix;
public class TestLocalInnerClass {
//int a; 外部类的成员变量
public void evaluation() {
//int b; 局部变量
class LocalInnerClass {
int c;
int d; //内部类的成员变量
private void quadrature() {
System.out.println(c+"*"+d+"的值:"+(c*d));
}
LocalInnerClass(int _c,int _d) {
c=_c;
d=_d;
}
}
//外部类成员方法中
LocalInnerClass in=new LocalInnerClass(10,20);
in.quadrature();
}
public static void main(String[] args) {
TestLocalInnerClass out=new TestLocalInnerClass();
out.evaluation();
}
}
内存分析:
局部内部类,顾名思义,和局部变量一样,都是在方法里面进行声明。局部内部类的作用域仅限于该方法,出来这个方法谁也不认识它了,所以要注意局部内部类的作用域。局部内部类可以访问外部类的所有成员(如a),成员方法的局部变量(如b),自身定义的成员变量就更不用说了。局部内部类除了不能再定义它的方法之外使用它之外,其实例化对象的方法和普通的类实例化对象的方法一样。要注意局部内部类不属于它的外部类,而是属于定义它的那个成员方法。
三、匿名内部类
例题分析:编写java程序,创建一个接口,在该接口中定义一个方法,然后创建一个类,在类中定义一个形参为接口类型的方法,并调用接口中的方法,在类的方法中创建一个实现接口的匿名内部类,用于实现接口中的方法。
接口
package com.XueFeng.NithPointSix;
public interface Animal {
public void cry();
}
匿名内部类测试
package com.XueFeng.NithPointSix;
public class TestAnonymousInnerClass {
public void method(Animal animal) {
animal.cry(); // 调用匿名类重写后的cry()方法
}
public static void main(String[] args) {
TestAnonymousInnerClass outClass=new TestAnonymousInnerClass();
Animal animal=new Animal() {
@Override /*@Override类似于注释,编译器忽略,如果不加,即使方法名
写错了也不会报错,所以重写方法时最好写上*/
//重写的方法最好复制接口里面相对应的方法,这样不容易写错
public void cry() {
System.out.println("汪汪汪......");
}
};
outClass.method(animal);
}
}
参考答案
public class ImpInterTest {
public void showInfo(InterTest t){
t.printInfo();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ImpInterTest imp=new ImpInterTest();
imp.showInfo(new InterTest(){
public void printInfo(){
System.out.println("这里是使用匿名内部实现了接口中的方法。");
}
});
}
}
参考答案直接将匿名内部类当作showInfo的实参传递给InterTest类型的形参,这样做更加明确地体现出匿名类不需要再创建对象时为对象起名。而我根据书中的做法,这样做在创建对象时为对象起了个名字“animal”。
匿名内部类创建对象时,调用的是默认构造函数。在匿名内部类的末尾处需要加上”;”,但这不是代表这个类代码块的结束,而是代表创建类对象的引用的表达式的结束标识。
四、静态内部类
声明静态内部类和声明静态变量相似,即在普通内部类前加上关键字”static”。静态内部类中,我们无法使用外部类的非静态的变量和方法,下面以一个静态成员内部类为例:
public class TestStaticClass {
int a=10;
static class staticClass {
int b=20;
public void print() {
//System.out.println("a="+a); //这里这样使用a将会出现错误
TestStaticClass out=new TestStaticClass();
System.out.println("a="+out.a); //但是可以这样使用,就像在静态方法main中
System.out.println("b="+b);
}
}
public static void main(String[] args) {
staticClass in=new staticClass(); //创建一个静态内部类对象
in.print();
}
}
虽然我们无法在静态内部类中直接使用外部类的非静态成员变量和成员方法,当可以先创建一个外部类的对象的引用,用对象.成员变量(方法)来使用,这个类似于主方法main()。从上面的代码我们同样可以发现,静态内部类和普通内部类的不同之处在于:静态内部类对象的引用的创建不需要依赖于外部类对象,也就是说,我们可以像创建外部类的对象的引用那样创建内部类对象的引用。
关于静态内部类,由于它的限制较多,所以编程中一般较少使用静态内部类。
内部类的继承
与普通类的继承一样,内部类同样存在着继承,但内部类的继承方式比普通类的继承方式要复杂。需要使用“外部类.内部类”的形式,还要有特定的构造方法。
package com.XueFeng.InnerClassInherit;
public class Test extends A.B{ //继承A的内部类B
public Test(A b) {
b.super(); //使用关键字super调用
}
public static void main(String[] args) {
A b=new A();
new Test(b);
}
}
class A {
class B{
public void print() {
System.out.println("Test继承了B");
}
public B() {
System.out.println("调用了内部类B的构造方法");
}
}
public A() {
System.out.println("调用了外部类A的构造方法");
}
}
注意Test构造方法的参数类型是外部类A的类型,要使用b.super调用类B的构造方法。
学习遇到的困难之处
虽然已经明白什么是内部类,但对于”内部类有什么作用,应该什么时候用,怎样用“还是不了解,这需要在日后的学习中注意这一点。