《Thinking In Java》内部类
定义:可以将一个类的定义放在另一个类的内部,这就是内部类。
创建内部类
public class OuterTest1 {
private String name = "王天逾";
//创建内部类
class InnerClass1{
int i;
InnerClass1(int i){
this.i = i;
}
public void f1(){
System.out.println("姓名:"+name+" 年龄:"+this.i);
}
}
public InnerClass1 getInnerClass1(){
return new InnerClass1(22);
}
public static void main(String[] args) {
OuterTest1 outerTest1 = new OuterTest1();
outerTest1.getInnerClass1().f1();
}
}
打印结果:
姓名:王天逾 年龄:22
外部类有一个方法getInnerClass1()返回一个内部类的引用。在内部类有个普通方法f1(),它可以访问外部类变量name,即使它在外围类中是private修饰符。也就是说,当生成一个内部类对象时,此对象与制造它的外围类对象直接就有了联系,它能够访问外围类对象的所有成员,内部类还拥有其外围类的所有的访问权。
.this 和 .new用法
- .this用法介绍
如果你想在内部类中返回外部类对象的引用,可以使用外部类的名字.this
public class OuterTest1 {
public void f1(){
System.out.println(".this用法。。。");
}
class InnerClass{
public OuterTest1 getOuterObject(){
return OuterTest1.this;
}
}
public InnerClass getInnerInstance(){
return new InnerClass();
}
public static void main(String[] args) {
OuterTest1 outerTest1 = new OuterTest1();
outerTest1.getInnerInstance().getOuterObject().f1();
}
}
- .new用法介绍
有时候你想要告知某些其他对象去创建其某个内部类对象,要实现这个目的,就必须在new表达式中提供对其外部类对象的引用,这就需要.new用法
此时删掉getInnerInstance()方法,在main()中创建内部类对象,看一下代码
public class OuterTest1 {
public void f1(){
System.out.println(".new用法。。。");
}
class InnerClass{
public OuterTest1 getOuterObject(){
return OuterTest1.this;
}
}
public static void main(String[] args) {
OuterTest1 outerTest= new OuterTest1();
outerTest.new InnerClass().getOuterObject().f1();
}
}
注意:
这里必须使用外部类的对象来创建该内部类对象,之所以这么做,是因为内部类对象会默认连接到外部类对象中。但是,如果创建的是静态内部类,那么它就不需要对外部类对象的引用了。
对于后半句,来写一下。是可以编译运行的。
public class OuterTest1 {
static class InnerClass{
public void getSomething(){
System.out.println("这是静态内部类。。。");
}
}
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
innerClass.getSomething();
}
}
内部类与向上转型
public class OuterTest2 {
private class DestinationImpl implements Destination {
String s;
DestinationImpl(String s) {
this.s = s;
}
@Override
public void readDestination() {
System.out.println(s);
}
}
protected class ContentsImpl implements Contents {
@Override
public int value() {
return 1;
}
}
public Destination getDestination() {
return new DestinationImpl("崇明岛");
}
public Contents getContents() {
return new ContentsImpl();
}
public static void main(String[] args) {
OuterTest2 outerTest2 = new OuterTest2();
outerTest2.getDestination().readDestination();
outerTest2.getContents().value();
}
}
内部类实现了接口,得到了对此接口的引用,与向上转型为这个对象的基类,是一样的。
在方法的内部类(局部内部类)
public class OuterTest3 {
public Destination getDestination(){
class MyDestinationImpl implements Destination{
@Override
public void readDestination() {
System.out.println("111");
}
}
return new MyDestinationImpl();
}
public static void main(String[] args) {
OuterTest3 outerTest3 = new OuterTest3();
outerTest3.getDestination().readDestination();
}
}
MyDestinationImpl类是getDestination()的一部分,而不是OuterTest3的一部分。所以,在getDestination()之外是无法访问MyDestinationImpl的。return的时候使用了向上转型,因为Destination是MyDestinationImpl的基类。
在特定的作用域内的内部类
public class OuterTest3 {
private void f1(boolean b){
if(b){
class MyClass {
void f2(){
System.out.println("5201314");
}
}
MyClass myClass = new MyClass();
myClass.f2();
}
}
public static void main(String[] args) {
OuterTest3 outerTest3 = new OuterTest3();
outerTest3.f1(true);
}
}
MyClass是被嵌入在if语句的作用域内,在定义MyClass类之外的作用域是不可用的,除此之外,它与普通类没区别。
匿名内部类
public class OuterTest3 {
public Contents getContents() {
return new Contents() {
@Override
public int value() {
return 520;
}
};
}
public static void main(String[] args) {
OuterTest3 outerTest3 = new OuterTest3();
System.out.println(outerTest3.getContents().value());
}
}
匿名内部类:创建一个继承自父类的匿名内对象,通过new表达式返回的引用被自动向上转型为对父类(这里是Contents)的引用。
将上面匿名内部类改成正常形式。
public class OuterTest3 {
public Contents getContents() {
return new ContentsImpl();
}
class ContentsImpl implements Contents {
@Override
public int value() {
return 520;
}
}
public static void main(String[] args) {
OuterTest3 outerTest3 = new OuterTest3();
System.out.println(outerTest3.getContents().value());
}
}
在这里父类Contents不需要接收参数,使用了默认构造,那么,想要为父类传递参数呢?
class Fu {
int i;
Fu(int i) {
this.i = i;
}
public int f() {
return this.i;
}
}
public class OuterTest3 {
public Fu getFuObject(int i) {
return new Fu(i) {
@Override
public int f() {
return super.f() * 100;
}
};
}
public static void main(String[] args) {
OuterTest3 outerTest3 = new OuterTest3();
System.out.println(outerTest3.getFuObject(2).f());
}
}
只需简单的传递合适的参数给基类的构造器就好了。
在匿名内部类中定义字段时,还能够对其进行初始化操作
public class OuterTest3 {
public Contents getContents(final int i) {
return new Contents() {
int ii=i;
@Override
public int value() {
return ii;
}
};
}
public static void main(String[] args) {
OuterTest3 outerTest3 = new OuterTest3();
System.out.println(outerTest3.getContents(100).value());
}
}
如果定义一个匿名内部类,并且希望其使用外部定义的对象,那么参数引用必须是final类型。
为匿名内部类创建"构造器效果"
匿名内部类不可能有命名构造器(因为它没有名字),但可以通过实例初始化,就能够达到为匿名内部类创建构造器效果。
abstract class BaseClass{
public abstract void f();
BaseClass(int i){
System.out.println(i);
}
}
public class OuterTest3 {
static class MyBase {
public BaseClass getBaseClass(int i){
return new BaseClass(i) {
{
System.out.println("inside");
}
@Override
public void f() {
System.out.println("anonymous");
}
};
}
}
public static void main(String[] args) {
MyBase myBase = new MyBase();
myBase.getBaseClass(10).f();
}
}
打印结果:
10
inside
anonymous
这里没有为参数设置final int i,是因为匿名内部类中没有使用i,只是传递给了父类构造。
嵌套类
定义:如果不需要内部类对象与其外围类对象之间有关系,那么可以将内部类声明为static
普通内部类对象隐式地保存了一个引用,指向创建他的外围类对象,然而,当内部类为static时,就不是这样了。
- 要创建嵌套类对象,并不需要其外围类的对象
- 不能从嵌套类的对象中访问非静态的外围类对象
普通内部类不能包含static数据和字段,而静态内部类则可以
public class OuterTest3 {
private static class MyContentImpl implements Contents{
private static int i=1;
@Override
public int value() {
return 100+i;
}
}
public static Contents getContents(){
return new MyContentImpl();
}
public static void main(String[] args) {
Contents contents = getContents();
System.out.println(contents.value());
}
}
在main()中,根本就没有生成外部类对象,而是使用static成员的普通语法来调用一个返回对Contents接口的引用。
接口内部的类
通常情况下,接口内部不能放置任何代码,但嵌套类可以,在接口里面,默认类是public static,由于是static,只是将嵌套类置于接口的命名空间内,这并不违反接口的规则。
interface ClassInterfaces {
void f();
class OuterTest3 implements ClassInterfaces {
@Override
public void f() {
System.out.println("接口内部类测试。。。");
}
public static void main(String[] args) {
new OuterTest3().f();
}
}
}
从多层嵌套类中访问外部类成员
一个内部类被嵌套多少层并不重要,它总能访问外部类的所有成员
public class MNA {
private void f1() {
System.out.println("f1");
}
class A {
private void f2() {
System.out.println("f2");
}
public class B {
private void f3() {
f1();
f2();
}
}
}
public static void main(String[] args) {
MNA mna = new MNA();
A a = mna.new A();
A.B b = a.new B();
b.f3();
}
}
内部类的继承
内部类的构造器必须连接到外围类的对象的引用,所以,那个指向外围类对象的引用必须被初始化,而导出类中在不再存在可连接的默认对象,要解决这个问题,需要使用enclosingClassReference.super();
class WithInner {
class Inner {
public void f() {
System.out.println("hh");
}
}
}
public class InheritInner extends WithInner.Inner {
InheritInner(WithInner i) {
i.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner inheritInner = new InheritInner(wi);
inheritInner.f();
}
}
通过i.super(),提供了必要的引用,然后程序编译通过。
内部类标识符
每个类都会产生一个.class文件,内部类也不例外.
外部类的名字+"$"+内部类的名字
如果是匿名内部类,编译器会简单的产生一个数字作为其标识符。
如果内部类嵌套在别的内部类中,只需将他们的名字加在外围类标识符与$的后面
声明
这篇博文是《Thinking in Java》内部类读书笔记。还有几个内部类知识点没有搞明白,这里没做笔记,等下次充分理解后,再补充。