1、抽象类与方法

前面讲多态的时候,就讲到了当Animal类 被Cat类所继承、也被Dog类所继承、假设再来Pig类继承、Bird类继承。。。

这些比动物类Animal 具体一点点的类都继承Animal类,Animal类里面有speak方法,然后每一个子类都重写这个方法。

当使用多态时,虽然接收的是Animal 类型的变量,但是new的确实一个个子类。执行的虽然是Animal类里面的speak方法名,但是执行的逻辑确是new出的子类中重写的speak方法逻辑。这个时候我们发现,Animal类里面这个speak的逻辑根本就不需要嘛。我们只是需要这个名字来统一规范,大家都重写它。

当一个类里面的方法直接不定义函数体时,这个方法就是抽象方法。含有抽象方法的类就是抽象类。

抽象类的出现就是我们实际写代码发现的规律。有些时候,子类抽取的公共方法到基类,基类只需要定义方法名即可,具体实现由子类重写就好了。这样自然而然就出现了抽象这个词了。抽象就是没有具体的实现的意思。

从以上可以得出,抽象方法必须要能被继承,不然没有意义!所以抽象方法不能是private、也不能是static 因为static是类的东西,也不能被继承。也不能和final同时修饰方法或者类,final修饰的类不能被继承,方法则不能被重写。

抽象的关键字是 abstract

abstract 只能修饰 类、方法。

public abstract class Animal {public abstract void speak();
}
  • 抽象类 可以没有抽象方法。

  • 有抽象方法的类一定要用 abstract 修饰,即有抽象方法的类一定是抽象类。

我们猜测一下 abstract 干了些啥。

1、abstract 修饰方法,这样就告诉编译器 我是抽象的,你不要检查我的函数体。

2、abstract 修饰类,就是告诉编译器,我里面可以定义抽象方法,你不要阻止我定义抽象方法。我内部定义的没有函数体的方法,给我检查一下是否使用了abstract修饰。

3、abstract 修饰类,就是告诉编译器,这个类不可用new出来,即不能实例化对象。本身父类即现在的抽象类就不是用来new的,而是用来接收子类new出的对象的。

既然抽象类不能new对象,那么是否有构造器呢?

必须有啊,子类继承的时候得调用啊。不然怎么拷贝父类的东西。这里也证明了一点,调用构造器与实例化对象是2回事!

讲抽象,不讲多态就是耍流氓。

定义了抽象类,就像是定义了规则,标准。具体的实现是交给了继承它的子类。

继承它的子类又分2种。

1、只重写了部分的抽象方法。

只重写了部分的抽象方法的子类,说明里面还有抽象方法。这还是一个抽象类!不能实例化对象

2、全部重写了抽象方法。

全部重写抽象方法,这才不是抽象类了,才可以new 对象。而且为了体现抽象类的作用,得用多态写法,用抽象类接收。用自己的类接收,将失去抽象类定义的意义!但是是可以用自己的类型接收。抽象是由多态进一步演化而来的。

2、匿名类

前面讲了匿名对象,即new 一个对象,但是这个对象不赋值给 某个类类型的变量,即不取名字。

一般是用来做实参直接传递给方法。详情见匿名对象部分。

匿名类实际上差不多。也是类不取名字,直接用。见如下代码:

// 有一个抽象类 Animal
public abstract class Animal {// 抽象方法 speakpublic abstract void speak();
}
​
@Test
public void test1(){// testFunc的参数 传递的是一个匿名类testFunc(new Animal() {@Overridepublic void speak() {System.out.println("匿名类,使用");}});
}
// 方法,参数是 Animal类型
private void testFunc(Animal ani){ani.speak();
}

上面的代码展示了匿名类的使用。

// 匿名类的定义
new Animal() {@Overridepublic void speak() {System.out.println("匿名类,使用");}
}

Animal 类是一个抽象类,是不可以直接new 对象的。但是你看匿名类,它的使用形式确实有new Animal(),这是矛盾的吗?

不是的,因为你不要忽略了后面还有大括号。大括号里面有对抽象方法speak的重写。

什么时候会出现重写呢?子类啊,我们只讲过 子类继承父类,可以重新父类里面的方法。

所以这个匿名类实际上是 Animal的一个子类。为什么用new Animal(){...}这种样子呢?因为这是个匿名类,没法用new自己的名字这种方法创建对象。只能用父类的。而且子类new对象时,会隐含的调用super方法,即父类的构造方法。Animal()实际上是Animal的构造方法。

当使用父类的名字进行new对象时,必须后面跟上{},并在里面重写抽象方法。这样才是new的匿名子类。

匿名类个匿名方法一样,只能用一次,因为没有名字,后面就找不到了,会被垃圾回收。

3、模板方法 设计模式

这个设计模式其实我们已经推理过了。就是多态的应用。

抽象类作为父类,里面定义抽象方法。是所有子类需要实现的方法。父类中的方法实际上是定义标准,即所有子类重新的名必须和父类定义的可重写的方法名称一致。

抽象类并没有规定所有的方法都必须是抽象方法,甚至全部不是抽象方法都可以。有部分抽象方法也行。

当我们解决一个问题时,某些步骤需要根据具体情况而定,但是步骤名称是确定的。就可以定义为抽象方法,放子类去重写,完善逻辑。

public abstract class Animal {// 抽象方法public abstract void speak();// 实际问题public void spendTime(){long start = System.currentTimeMillis();speak();long end = System.currentTimeMillis();System.out.println("花费了:"+ (end - start)+"这么长的时间!");}
}
// 一个非抽象子类
public class Dog extends Animal{public String type = "Dog";
​@Overridepublic void speak(){System.out.println(this.type);}
}
​
// 使用
@Test
public void test1(){Animal dog = new Dog();testFunc(dog);
}
private void testFunc(Animal ani){ani.spendTime();
}
​

上面代码中,实际问题是 求 speak方法执行了多少时间。这个speak()得根据具体的 子类实现确定。但是整体步骤是确定了。即spendTime方法的逻辑是确定的。

将speak定义为抽象方法,有一个子类Dog继承这个Animal类,并实现这个speak方法。

当我们用多态写法 用父类接收new出来的Dog时,传递给testFunc。Func里面调用Animal里面的spendTime方法可以了,spendTime方法里面调用的speak方法实际上是Dog类里面重写的speak方法。这是以前讲的多态与方法重写。

这样与以前的区别是什么?

以前testFunc是直接调用的父类的抽象方法speak,现在是调用父类里面一个含有抽象方法的方法spendTime。就是在speak方法上增加了一点点逻辑,将抽象的speak方法包装了一下,变成了含有speak方法的spendTime方法。本质还是一样的。

这个模式其实就是多态的应用。

4、接口 interface

为什么需要接口这个东西?

JAVA里面的接口与类是同级别的东西。你有没有发现,抽象类的定义是里面的方法可以是抽象的,也可以是不抽象的,但是含有1个抽象方法的类必须是抽象类。这个抽象类里面的方法没有定义死。即有些方法可以是不抽象的!

接口就是升级一下。

1、所有方法都必须是抽象的!规定我这个类里面所有方法都是抽象的,即我不再提供具有函数体的方法了。这个类定义的方法都是一种标准和规范了。子类都得按照已经定义好的方法名称来重写。

2、接口还做了一个升级,继承接口的类必须全部重写里面的抽象方法。抽象类的子类是没有这个规定的。

3、类还有一个规定,类必须是单继承的。而接口突破了这个限制,可以多继承,或者说为了提供多继承的需求,又不与类冲突。将多继承的功能赋予了接口。

满足了这些条件,抽象类就升级改名叫接口了。

接口的所有方法都必须是抽象的吗?

在JAVA 8之前确实是这样的,jdk8之后接口也允许定义有函数体的方法了。但是这些有函数体的方法又限制,必须是默认方法或者静态方法。在JDK9 之后 接口又增加了可以定义私有方法的特性。

可见,语言是在发展中的,JAVA设计者一开始也不能完全的设计好一门语言。需要在实践中不断的改进。但是改进的前提是不能破坏原有的功能。这就是为什么以前规定 接口中的方法必须都是抽象的,而且必须都能被继承(实现)。而java9之后规定你要定义非抽象方法也可以,必须是默认方法、静态方法、私有方法。

分析一下:默认方法的由来。

public interface Demo1 {// 首先是默认方法的定义 使用defaultpublic default void method2(){System.out.println("Hello");}
}

前面分析到 接口中的方法最开始规定是必须都是抽象的,必须都能被继承,被重写的。而且要全部重写的。如果接口由于业务需求突然增加了1个抽象方法。那么继承(实现)它的很多类都会报错,因为还有一个抽象方法没有实现。

为了解决这个问题,出现了默认方法。这个默认方法就不强制要求需要子类去重写,因为自己有方法体。子类也是可以进行重写的。

Collection 接口里面就有 默认方法。如 stream、parallelStream、spliterator、removeIf
Collection 继承了Iterable接口,并且重写了 spliterator 方法。你没看出,接口也可以重写父接口的方法。前提是一定要定义为默认方法。因为静态方法不能被继承,只剩下默认方法可以有方法体。

静态方法:

静态方法就比较简单了,凡是静态static就代码归属权发生变化。这个方法将属于接口,而不是对象。也不能被继承(实现),只能通过接口名点的方式去调用。与类的静态方法也有点不一样。普通类是可以进行实例化的,所以可以通过类的对象去方法类的静态方法。但是接口不能实例化对象。只能通过接口名访问。

静态方法的出现也是实践中发现,有时候会有一些公共的方法。抽取到接口比较合适

私有方法:

暂时略过。属于JDK9的内容。本教程针对JDK8以及以下。

4.1 接口的属性

接口中的属性只能是全局常量,按照接口的出现来说,比抽象类抽象更彻底,必须被子类继承实现。自己本身不能实例化对象。

不能实例化对象,意味着接口中的属性不能被自己的对象调用。只能被子类的对象调用。所以接口中的属性必须能被继承,不能是private。自己不能实例化对象,接口想要自己用,就必须定义为static。

接口中的方法现在都变成了规范了。所以属性要么没有,要么也要变成一种规范。即定义了规范,你们只能用,不能改。也要定义为final

所以最开始的 接口属性定义为 public static final,翻译就是能被继承,通过接口名可以访问,子类只能用不能改

4.3 接口与类的探讨

4.3.1 接口有构造函数吗?

接口中是没有构造方法的。因为接口抽象的很彻底。不能实例化对象。而且支持了多继承。JAVA设计者将构造方法给拿去了。

但是子类还是要继承的,要重写抽象方法的,不是说子类会默认调用父类的构造方法吗?

是的,那是继承才会。现在既然没有构造方法了,子类继承接口也将不再叫做继承。而叫做实现了。

前面为了说明来由,将继承与实现同化了。事实上是有区别的。

子类继承接口改名叫做实现了!接口里面的继承都是指实现。类与接口的关系没有继承,只有实现!!!

原因是实现不会默认调用接口的构造方法,因为接口就没有构造方法!

4.3.2 接口与类的关系

继承的关键字是 extends 而实现叫做 implements

纠正:本篇文章里面,上面写的关于 类继承接口 的说法其实是不对,正确的应该是类实现接口。之所以说继承是为了和类的继承进行对比。事实上,接口的实现和类的继承区别就不大。继承会默认调用父类的构造方法,而实现不会。其余的都一样。

类与类的关系:继承、而且是单继承

类与接口的关系: 实现,是类实现接口。

接口与接口的关系:继承,而且可以多继承。

同种类型之间叫做继承,类与接口之间才叫实现。

接口的多实现:

这个没什么新意。本来就是要这样子的。在多态里面,父类被很多子类继承,每个子类重写父类方法。慢慢的发现父类的被继承方法不需要方法体,由此引出抽象方法,又引出抽象类。抽象类不够彻底。来了接口。

这个演化的过程只是父类的演化。实质上还有需要很多子类去继承的。当然,接口里面改叫实现了。

没有多个实现类去实现接口,就没有接口的多态。

注意: 继承和实现是可以同时存在的。

问题:当实现类继承的父类与实现的接口具有同名同参数的方法时,而实现类又没有重写这个方法,调用的是哪一个的呢?

当这个方法在接口中不是默认方法时,即JDK7之前的接口定义,是没有方法体的,是一个抽象方法。而父类中这个方法如果是抽象方法,继承这个方法一定要重写的,没有重写说明不是抽象方法。即这个方法是有方法体的。所以,这种情况下是调用的父类的方法。

JDK8增加了默认方法,但是也不能改变这种调用原则。看起来像是 类优先

4.3.3 为什么接口要支持多继承(实现)?

先看看为什么类不支持多继承。假设支持。

一个类A同时继承B、C、D三个类。B、C、D 都有speak方法。

当A没有重写speak方法时,A实例化一个对象,调用speak方法,请问调用是哪一个类里面的speak方法呢?

是B还是C又或者是D呢?

这个问题是有算法解决的,但是很复杂。C++支持多继承,python也支持。可以去参考一下。

这里如果不能确定调用的是哪一个方法,就会带来执行结果的不确定性。JAVA将这种情况给禁止了。

那么,为什么接口又允许了呢?

最大的原因就是接口的方法抽象的很彻底。1.7之前的方法都必须是抽象的。没有函数体的。

实现类必须重写接口中的方法。

看这种情况:一个类A同时实现B、C、D三个接口,这三个接口都有speak抽象方法。

由于A必须重写speak,所以调用的一定是自己的逻辑。不存在类的多继承这种情况。

即使三个接口B、C、D的抽象方法重名也没关系,反正没有方法体,而且都会被1个同名方法重写。

这样接口的多继承变成了接口简单的抽象方法合并。多个同名抽象方法相当于1个生效。

JAVA8的接口支持默认方法带来了一些新的问题。

问题:当实现类实现的的B、C、D里面都有同名同参数的默认方法时,A实现类调用这个方法是调用哪一个接口的默认方法呢?

ans:接口的默认方法原本是不要求实现类一定要重写。但是这种情况,必须进行重写。不然就会冲突。不知道调哪一个接口的。

当重写了之后,就是调用自己重写的,而不是调用接口中的。

再深入一下:如果还继承了 E类,E里面也有同名同参数的这个方法,是如何调用的呢?

ans:此时反而简单了,见上面的类优先原则。是调用父类E里面的这个方法。而且子类不要求必须重写。

当然,实现类要是重写了,还是调用重写的方法。

4.4 接口的匿名实现类

这个没啥新东西了。

匿名还是用在作为参数传递的时候用。看下面例子。

// 定义 Animal 接口
public interface Animal {
​// 定义 speak 抽象方法public void speak();
}
​
@Test
public void test1(){testFunc(new Animal() {@Overridepublic void speak() {System.out.println("匿名实现类");}});
}
private void testFunc(Animal ani){ani.speak();
}

上面的代码与匿名类的实现其实没什么不同。都是一般用在作为方法的参数传递。接口就是new 接口名,加上一对大括号,大括号里面全部实现接口的抽象方法,实现的本质还是全部重写抽象方法。

对比一下抽象类的匿名子类:

// 有一个抽象类 Animal
public abstract class Animal {// 抽象方法 speakpublic abstract void speak();
}
​
@Test
public void test1(){// testFunc的参数 传递的是一个匿名类testFunc(new Animal() {@Overridepublic void speak() {System.out.println("匿名类,使用");}});
}
private void testFunc(Animal ani){ani.speak();
}

看到没有,区别就是一个是接口一个是抽象类。能实现的功能都一样。

接口就是来源于抽象类。属于特殊的抽象类。

5、代理模式(Proxy)

代理模式:为其他对象提供一种代理,以控制对这个对象的访问。

上面这句话很抽象,你肯定不懂,肯定记不住。哈哈

不着急。

先看看模板方法的代码:

public abstract class Animal {// 抽象方法public abstract void speak();// 实际问题public void spendTime(){long start = System.currentTimeMillis();speak();long end = System.currentTimeMillis();System.out.println("花费了:"+ (end - start)+"这么长的时间!");}
}
// 一个非抽象子类
public class Dog extends Animal{public String type = "Dog";@Overridepublic void speak(){System.out.println(this.type);}
}// 使用
@Test
public void test1(){Animal dog = new Dog();testFunc(dog);
}
private void testFunc(Animal ani){ani.spendTime();
}

这种模式 就是 我们实现在抽象方法上增加一点逻辑。事实上还是要调用这个抽象方法的。

再来看看代码模式的代码:这里写的是静态代理。动态代理需要反射知识。到时再补充。

// 定义一个 接口 里面有一个抽象方法。
public interface NetWork {// browse 抽象方法void browse();
}
// 定义被代理类,这个类是 接口的实现类
public class ServerImpl implements NetWork {@Overridepublic void browse() {System.out.println("Server实现类 实现browse方法");}
}
// 定义代理类,这个类也是接口的实现类
public class ProxyServer implements NetWork {// 接口类型的变量 接收的一定是 接口的实现类,因为接口不能实例化对象private NetWork work;@Overridepublic void browse() {System.out.println("代理类实现方法");check();this.work.browse();}// 检查方法public void check() {System.out.println("检查方法");}public ProxyServer(NetWork work){this.work = work;}
}// 测试使用
@Test
public void test1(){// new的是代理类,传的参数是被代理类对象ProxyServer proxyServer = new ProxyServer(new ServerImpl());// 执行的是代理的方法。proxyServer.browse();
}

以上代码中,先有一个接口NetWork,里面有一个browse方法。 被代理类 ServerImpl 必须实现这个方法,因为它要真正的干活。

而代理类 ProxyServer 也是 NetWork 的一个普通实现类。也必须实现 browse 方法。只不过,他实现的有所不同。他重写browse的逻辑是内部先执行了自己的check方法。然后执行了 被代理类的 browse。

@Override
public void browse() {System.out.println("代理类实现方法");check();this.work.browse();
}

具体是 在构造函数中接收一个对象,用多态方式接收,即形参是接口类型。是接口类型的形参可以接收任意实现类的对象。也包括他自己哦。但是不能真的传递他自己,会死循环。接收到的对象保存到类属性work中。

然后在重新browse的时候,先执行需要增加的逻辑 check,再调用 接收到的对象的browse方法。就像你委托别人帮你买房,别人去买房的时候增加了看房,讨价还价等逻辑,再叫你自己去付款,然后他可能再继续 搞其他手续。

在测试使用中,我们看到,new的是代理类对象,传递的是被代理类的一个匿名对象,你也可以传递普通的有名对象。

然后执行的是代理类的browse方法。看起来是代理类在干活。实际上,代理类干的活里面包括了你要干的活。

和前面的模板方法都实现了在不确定实现的基础上增加确定的逻辑。

模板方法是直接在抽象类中干的,因为抽象类是可以有非抽象方法的。但是接口不允许。所以只能在某一个实现类里面干。这个实现类就是代理类。

下面再看一个基于 抽象类 的代理模式:

// 抽象 类Animal
public abstract class Animal {// 抽象方法 speakpublic abstract void speak();
}
// 子类 dog 继承 Animal
public class Dog extends Animal{public String type = "Dog";@Overridepublic void speak(){System.out.println(this.type);}
}
// 子类 代理类 继承 Animal
public class AbstractClassProxy extends Animal{private final Animal ani;@Overridepublic void speak() {long start = System.currentTimeMillis();ani.speak();long end = System.currentTimeMillis();System.out.println("花费了:"+ (end - start)+"这么长的时间!");}AbstractClassProxy(Animal ani){this.ani = ani;}
}// 测试使用@Test
public void test1(){AbstractClassProxy proxy = new AbstractClassProxy(new Dog());proxy.speak();
}

用抽象类来实现代理模式也是可以的。但是总感觉有点别扭。

因为Animal类的子类应该都是动物,这里搞了一个AbstractClassProxy 很怪异。因为AbstractClassProxy不是一种动物。这也就是大家普遍认为 类与子类之间要有一种关系。即子类要归属于父类的一种。而不是像这里的这样 谈不上联系。

对于这样 没有什么关系的继承最好使用 接口来做。因为接口不叫做继承了,改叫 实现。就没有了这种 is a关系。或者说弥补了 is a关系以外的关系。

6、工厂模式

当没有工厂是,创建者和调用者都在一起。

public interface Car {// run 方法void run();
}
public class AudiImpl implements Car {@Overridepublic void run() {System.out.println("奥迪跑");}
}
public class BydImpl implements Car {@Overridepublic void run() {System.out.println("byd 在跑");}
}
public class TestFunc {@Testvoid test1(){AudiImpl audi = new AudiImpl();BydImpl byd = new BydImpl();audi.run();byd.run();}
}

如上面的代理,创建 audi 与byd 和他们调用都在一起。这是常规用法。

如果将创建对象与对象调用分开,那就是工厂模式了。

6.1 工厂方法模式

增加一个工厂接口,里面有一个抽象方法

public interface CarFactory {// 获取CarCar getCar();
}

现在提供具体的实现类。来创建不同的对象。

public class AudiFactoryImpl implements CarFactory {// 注意 重写方法的返回值可以是 接口定义的返回值本身或者子类// 这里直接写Car作为返回值也没有什么问题@Overridepublic AudiImpl getCar() {return new AudiImpl();}
}
public class BydFactoryImpl implements CarFactory {// 这里就是保留原来的返回值@Overridepublic Car getCar() {return new BydImpl();}
}

使用:

AudiImpl audi = new AudiFactoryImpl().getCar();
Car byd = (BydImpl)new BydFactoryImpl().getCar();

这样还不是最完美的,因为没增加一种骑车,就需要增加一个具体的工厂实现类。当学习到反射时,就可以突破这个限制。

6.2 抽象工厂模式

这个就是比工厂方法模式在创建对象的复杂程度更高一些。

用意再用给客户端提供一个接口,可以创建多个产品族中的产品对象。

具体的讲解将在 设计模型 专栏讲解。

7、内部类

就是一个类声明在另一个类的内部。这与同一个文件写多个类是有区别的。

写在内部意味着 写在某个类定义的大括号里面。外面的类叫做外部类,里面的就是内部类。

为什么会出现这种东西呢?

就是我们定义属性时,简单的属性不能满足我们的需要。需要复杂的数据类型。

复杂的数据类型就是类啊。

其实内部类放在外面也是可以的。之所以放在内部,是因为这2个类关系密切。

public class InnerClassDemo {private InnerC inner;// 普通成员内部类class InnerC {}// 静态成员内部类static class StaticInnerC{}
}

看上面的代码,定义了2个成员内部类,一个静态的,一个非静态的。还可以定义局部内部类。

public class InnerClassDemo {private InnerC inner;// 普通成员内部类class InnerC {}// 静态成员内部类public static class StaticInnerC{}public void method() {// 局部内部类class LocalInnerC{}}{   // 代码块内部类 也是局部内部类class CodeBlockInnerC {}}
}

一般用的多的是成员内部类。

从2方面分析成员内部类:

  • 作为类的成员:能被 权限、static、final、abstract修饰。

4中权限都可以修饰。注意外部类只能是public 或者缺省

static、final修饰都是与属性、方法一致的。

  • 作为一个类:具有所有类的特性。

内部类的例子:

例如:Integer 包装类里面就有 一个 IntegerCache 内部类。

7.1 如何实例化成员内部类?

如果要在外部实例化成员内部类,要求这个内部类外部可以访问。即类的权限为public,还得是静态的

InnerClassDemo.StaticInnerC staticInnerC = new InnerClassDemo.StaticInnerC();

对于非静态的内部类,需要先实例化外部类对象,再new出来。注意是 对象.new 内部类名称

InnerClassDemo innerClassDemo = new InnerClassDemo();
InnerClassDemo.InnerC innerC = innerClassDemo.new InnerC();

7.2 如何在成员内部类中区分调用外部类的结构?

public class InnerClassDemo {private InnerC inner;private String name = "外部类";// 普通成员内部类public class InnerC {private String name = "普通内部类";}// 静态成员内部类public static class StaticInnerC{private String name = "静态内部类";}public void method() {// 局部内部类class LocalInnerC{private String name = "方法局部内部类";}}{   // 代码块内部类 也是局部内部类class CodeBlockInnerC {private String name = "代码块局部内部类";}}
}

上面展示了 所有类都有同名的name属性,来看看如何区分调用

public class InnerClassDemo {private InnerC inner;private String name = "外部类";// 普通成员内部类public class InnerC {private String name = "普通内部类";public void test(){// 调用外部类的属性 nameSystem.out.println(InnerClassDemo.this.name);}// 测试public void test(String name){System.out.println(name); // 调用方法形参System.out.println(this.name); // 调用本类的属性System.out.println(InnerClassDemo.this.name); // 外部类的属性}}// 静态成员内部类public static class StaticInnerC{private String name = "静态内部类";}public void method() {// 局部内部类class LocalInnerC{private String name = "方法局部内部类";}}{   // 代码块内部类 也是局部内部类class CodeBlockInnerC {private String name = "代码块局部内部类";}}
}

在 普通的背部类中测试了一下。this的用法还是不变。非静态结构还是不能调用静态结构。

内部类调用外部类属性要用 外部类名.this.属性名的方式

7.3 局部内部类的使用

局部内部类

public void method() {// 局部内部类class LocalInnerC{private String name = "方法局部内部类";}}

上面这种使用方法很少见。一般是需要获取一个对象时才会用到。见下面的调用

// 获取一个实现了Comparable接口的类对象// 方式1:public Comparable getComparable1(){// 创建一个Comparable接口的类, 这个类就是局部内部类class MyComparable implements Comparable{@Overridepublic int compareTo(Object o) {return 0;}}return new MyComparable();}// 方式2:public Comparable getComparable2(){// 匿名实现类 这也是内部类return new Comparable(){@Overridepublic int compareTo(Object o) {return 0;}};}

你会发现,匿名类可以替换有名类,本来就是内部用一次。所以方式2见的多。

上帝视角学JAVA- 基础08-类06【2021-08-07】相关推荐

  1. 【零基础学Java】—Socket类(五十五)

    [零基础学Java]-Socket类(五十五) Socket类:该类实现客户端套接字,套接字是指两台设备之间通讯的端点. 在Java中,提供了两个类用于实现TCP通信程序 客户端:java.net.S ...

  2. 【零基础学Java】—System类(三十五)

    [零基础学Java]-System类(三十五) java.lang.System 类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作 在System类的API文档中,常用的方法有: pub ...

  3. 【零基础学Java】—Calendar类(三十四)

    [零基础学Java]-Calendar类(三十四) java.util.Calendar日历类 Calendar类是一个抽象类,里面提供了很多操作日历字段的方法 Calendar类无法直接创建对象,里 ...

  4. 【零基础学Java】—Scanner类的使用( 十)

    [零基础学Java]-Scanner类的使用( 十) Scanner类的功能:可以实现键盘输入数据,到程序中去. 引用类型的一般使用步骤: 1️⃣ 导包 import 包路径.类名称: 如果需要使用的 ...

  5. Java基础|1-07-Object类与常见API(二)@API篇

    写在前面: 此系列文是笔者在学习Java系列课程的过程中,参考相关课件.视频讲解.课程代码,并结合一些文档.思维导图及个人理解,对所学内容做的阶段性梳理与总结. 写于:2021年1月28日 内容:Ja ...

  6. JAVA基础七 类和对象

    文章目录 JAVA基础七 类和对象 01 引用 02 继承 03 方法重载 04 构造方法 05 this 06 传参 07 包 08 访问修饰符 09 类属性 10 类方法 11 属性初始化 12 ...

  7. Java基础-Date类常用方法介绍

    Java基础-Date类常用方法介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.毫秒值概念 我们在查阅Date类的API文档时,会发现这样的一句话:"The cl ...

  8. java基础-BigDecimal类常用方法介绍

    java基础-BigDecimal类常用方法介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.BigDecimal类概述 我们知道浮点数的计算结果是未知的.原因是计算机二进制 ...

  9. Java基础18-String类【String类的特点对象个数常用方法】【超详细讲解】

    Java基础-String类[超详细讲解] String类的特点 String在java.lang.String包中 1:特点 (1)String类型不能被继承,因为由final修饰 (2)Strin ...

  10. JAVA基础––从类和对象开始

    JAVA基础--从类和对象开始 一.概念 1.面向对象中类和对象的概念 对象:对象是具有状态和行为的实体,例如某个具体的学生,或者是王者荣耀里的英雄,可以是物理实体,也可以是逻辑实体. 类:类是一组具 ...

最新文章

  1. Android Framework增加API 报错 Missing nullability on parameter
  2. Manjaro 软件源及软件管理相关操作【pacman、pacman-mirrors】整理
  3. 如何正确使用数据可视化图表
  4. vue中生产模式和调试模式_为什么在生产中进行调试是如此诱人?
  5. C语言实现MATLAB 6.5中M文件的方法
  6. Xcode6 管理provisioning profile
  7. 2020年11月 Oracle WebLogic 高危预警:CVE-2020-14750 无需认证攻击
  8. Windows下Apache架站务实
  9. IDEA14创建Maven管理的Java Web项目
  10. 在Spring Boot中使用 @ConfigurationProperties 注解, @EnableConfigurationProperties
  11. 中国电信联合多方加速推进OpenStack技术产业化 —— OSCAR即将发布“OpenStack技术应用场景”技术规范...
  12. 3.3 CMMI3级——技术解决方案(Technical Solution)
  13. Java 枚举类转换List
  14. 在QQ浏览器打开html,QQ浏览器显示网页打开错误的解决方法
  15. linux如何使用磁盘阵列卡,Linux的RAID磁盘阵列与阵列卡
  16. 博客迁移说明 : )
  17. Mac显示桌面的快捷方式
  18. Kubernetes 认证
  19. html中如何定义斜框,html表格单元格添加斜下框线的方法
  20. NOIP 2015 蒟蒻做题记录

热门文章

  1. 【uniapp】 读取手机通讯录权限
  2. ubuntu下启动wifi
  3. Win11 与 macOS 12 界面对比
  4. Linux命令篇:chmod 777 与 chmod +x
  5. Redis应用项目---抢红包功能(四)
  6. 【信号处理】迫零均衡前与迫零均衡后眼图对比附Matlab代码
  7. 黑客养成—CTF笔记(一)
  8. [附源码]java毕业设计病历管理系统设计
  9. 棋牌游戏网站支付接口那些事儿
  10. 高斯模糊磨皮——ps