1java除了八个基本类型外,一起皆是对象(对象object 又叫实例instance)
和面向过程的区别
1、面向过程 自顶而下,需要顶层设计,而面向对象,可组装起来。方便维护,改一个地方不用从顶到下修改。
2、猪八戒吃西瓜
面向过程:吃(猪八戒,西瓜)
面向对象:猪八戒.吃(西瓜)
面向对象的优点
1、封装性:数据与操作方法封装到一起
2、自治性:一个对象要改变另一个对象,需要调用那个对象的方法,而不是直接改变这个对象。
3、安全性:隐藏内部的具体实现,仅提供一个黑盒子
4、拓展性:代码复用和方便拓展
面向对象三大特征
封装:抽象如提取父类,封装是数据和操作封装到一起,只有内部的方法能操作数据。访问控制符 包等;把该暴露的暴露出来,把该隐藏的隐藏起来
继承:软件设计规范,不要把一样的代码书写两次以上!
多态:实质是编译类型和运行类型不同 一个名字 多种功能
类----也叫抽象数据类型
类里的变量统称为成员变量、成员方法 具体又可分为
- 实例变量、方法
- 类变量、方法(注:类变量可被修改 加了final的才不能被修改)
类变量属于类,多个对象创建时不会重复创建这个值(即不会重复分配内存)(还可保持一致性)。类变量可通过类本身或者对象访问,但是修改只能通过类方法或直接类.类变量=多少修改。?修改似乎也可以通过对象?类变量最重要的意义在于保持一致性和减少资源消耗
类方法只能处理static域的变量,不能访问实例变量。
注:成员变量和成员方法名可以相同
类与对象 与包
对象可被syso打印出来,如a.A@sldkfjs
a是包名,A是类,@符号后是伪地址
类修饰符
-见修饰符那章
抽象类与抽象方法
来源
比如鸟,自然界并不存在一只实实在在的鸟,它既不是鸽子也不是燕子。所以,鸟仅仅作为一个抽象的概念,代表了所有鸟的共同属性。
当我们描述燕子时,通常会说这是xxx形状的鸟,所有,燕子的概念建立在假设对方知道什么是鸟的前提之上,只有进一步问鸟是什么的时候,才会具体解释说,鸟是一种长着翅膀会非的卵生动物,这就是抽象类的来源。
优点
比如开发一个电话功能,由于电话分固定电话、移动电话、又可具体分为手机、小灵通等等。
比如我们定义一个拨号操作,假设这个之前没有返回值,而我们现在想要知道是否成功拨打,因此要添加一个布尔返回值 true、false 。有了抽象类,我们就不需要具体改动每个子类,只需要改动抽象类本身即可。
规则
1、抽象类本身不能实例化,必须通过子类实例化,
2、抽象类的子类仍然可以是抽象类
3、抽象类中可以有抽象方法 也可有非抽象方法。但是抽象方法只能存在于抽象类中。
4、抽象方法仅含方法声明(名字、参数,返回值类型)部分,没有具体的方法体。抽象方法是形成若干个名字相同,返回值类型相同,参数列表相同,目的一直,但是具体实现有差别的方法。为abstract方法编写方法体也是没有意义的,因为子类会自己实现不同的方法。
5、抽象类的子类如果不是抽象类,则有义务实现抽象类中的所有抽象方法。
6、抽象方法是为了形成若干个名字、参数列表、返回值类型、目的相同,但是具体实现有去别的方法,为抽象方法编写方法体是没有意义的,因为它的子类会有不同的实现
注:
嵌套类
类中也可定义类,编译出来的文件名为:外部类名$内部类名.class
构造方法
class x = new construct(参数)
java会提供一个默认的无参数构造方法,但是当你自己定义了有参数的后,就不能用这种无参数的构造方法了,除非你再手动写一个无参数构造方法
继承
类的继承-extends
extends本意是拓展,这个解释其实更合适,拓展了父类!
Java中,除Object外,每一个类都有一个父类,Object是java中唯一没有父类的类。若不指定继承对象,则默认继承Object类
Java只支持单继承,接口可以多继承
继承时,必须程序元显式调用父类的构造器(父类也需要初始化),若不显式指定使用父类哪个构造器,则默认使用无参数构造器!(子类不会继承父类的有参数构造器)
继承不会继承父类private声明的东西
子类方法的重写要遵循两大一小原则,即返回值类型、异常类型比父类更小或者相等;子类方法的访问权限比父类更大或相等,否则会编译错误。
为了良好的封装,不让父类变量被子类随便改变,应该尽量设置父类的成员变量为private,以及一些不希望子类改变的方法设置为final
使用继承
this与super
避免用this的方法
public test(int _a){
a = _a
}
this表示当前类的当前对象本身,更准确的说,this代表了当前对象的一个引用。用于方法中,形参和实参同名的情况(实参会隐藏起来)
ps:this不能用于static方法,因为this代表的是对象,super也是如此
super(参数)----------表示直接父类构造器
super.参数-----------表示直接父类成员
域的隐藏与方法的覆盖
不同之处在于,前者仅仅是隐藏(子类定义了和父类相同名字的变量或构造方法 会把父类变量隐藏,可通过super访问),而后者会使父类方法在子类中不复存在
修饰符
类修饰符:
- public与非public,非public类只能被同一程序包的其他类使用。只要import引入public类,就可以使用这个类。
- abstract与非abstract类,被abstract修饰的类不能实例化,需要子类实例化
- final类与非final类,final声明该类不能被继承
成员变量访问修饰符
public 随便访问
protected 不同包非子类不可访问(子类和父类在不同包中,只有子类的对象能使用,父类本身也不行)
default 同包、同类可访问
private 同类可访问。注:同一个类的两个对象可以互相访问private成员,因为访问限制是在类的层面而非对象层面
方法修饰符
抽象方法与非抽象
类方法与实例方法,类static成员不能访问无static的成员
private方法
最终final方法–注,所有被private方法修饰的以及所有包含在final类中的方法都默认是final的方法
本地方法:native 在某些场合 可引入其他语言(如为了效率引入c)。除非保证引入的代码也是跨平台的,否则整个java程序的跨平台性可能遭到破坏
同步方法:会加锁,多线程时
层次:
import
可引入包、类
import a 表示引入a包
import a.A 表示引入a包的A类
import a.a1.A 表示引入a包的a1子包的A类
javac 的时候自动创建目录
自动引入lang和Math包和无名包
重载
重载即多个方法可以共享一个名字
重载需要参数个数、类型、或者顺序不一样
重载表面上没有减少程序的工作量,但是我们只需要记住一个方法名即可,使得程序看起来简单
多态
一个名字,实现方法包括覆盖(子类定义同名的父类方法)和重载(同一个类中 参数个数、类型、顺序不同实现)
构造方法的多态–覆盖与重载
普通方法的就不展开说了
当类中一个构造方法想调用另一个构造方法时,可用this(参数s)的形式
构造方法的继承-----子类不会继承父类的有参数构造器
1、若子类不在构造方法中使用super()显式调用父类的构造器:子类会默认继承父类的无参数构造器。(所以父类中如果写了带参数构造方法 一定要写个无参数构造器)
2、若子类在构造方法中显式使用了父类构造方法,则正常写即可(要写第一行),
接口
作用:
1、多继承:由于java单继承机制,越是下面的类,间接父类越多,要继承的东西越多,造成子类成员的膨胀冗杂。接口可以将完成特点功能的属性组装成相对独立的集合。这种集合就是接口
2、定义协议、规范:接口中的属性都是没有方法体的抽象方法,在java中,把继承接口称之为implement实现。
用法
【public】interface 接口名 【extends 父接口s】
public class 具体的类 implements 接口名
1、类在实现抽象方法时候,必须用publc修饰符
2、除了抽象类外,类的定义部分必须实现接口中所以的抽象方法。
3、接口不能被覆盖
4、接口中仅有方法 无变量
自动类型转换–实现继承的多态性(抽象类也可类似这么使用)
可以通过:接口名 对象名 = new 子类实现类构造方法 的形式创建对象
如 interface Speaker (定义了speak方法)和实现它的两个子类 Philosopher 和Dog
哲学家另外定义了一个放屁的方法,而狗另外定义了一个哈气的方法
Speaker current;
current = new Dog();
current.speak();
current = new Philosophy();
current.speak();
(Philosopher)current.fangpi();//由于Speaker接口中没有定义放屁这个方法,所以需要强制类型转换
current.hhh();//若直接使用这个哈气的方法,虽然此时的current是狗,但是也会错误,因为hhh不是Speaker定义的方法
一些其他的
类中可使用类本身,对象的方法中也可返回对象本身,其实返回的只是数据接口,有何不可呢
Java不允许吧方法作为参数传递到方法中,但是可以变通的将对象作为参数,然后再调用这个方法。
方法的返回值类型为某类时,返回的对象类型必须是这个类或者其子类;当为接口时,返回对象所属的类必须实现这个接口
类中可以重复使用本类,比如类中定义方法的参数类型为本类,有点像递归,其实只要把类理解为数据结构 又有何不可呢
构造方法不能被修饰符修饰,名字需要和类名一致,class x = new class(参数列表),后面的那个class实际上是构造方法名
没有名字的包统一进入“无名包” 算是一个包
当类名不冲突时候 可直接 类 x=new 类 当冲突时需要指定包名
构造方法的实质其实也是有返回值的 不过返回的是对象
总结 -面向对象的特征
1、封装:访问控制符、包的机制
2、类生成对象,构造方法、成员变量、成员函数
3、继承
4、接口
5、多态
6、抽象方法
单例类–保证只会创建一个对象以及把构造器隐藏起来的写法
Class Single{
private static Single instance;
private Single();//重写构造器 将其变成private 达到隐藏的目的
publc static Single getInstance(){
if(instance==null)
instance = new Single();//调用被隐藏的构造器 生成对象并缓存下来
}
return instance;
}
Single s1 = Single.getInstance();//不用构造器得到实例的方法
Single s2 = Single.getInstance();s1和s2指向相同
匿名内部类
这里说是匿名类 不如说是匿名对象!用于这个对象只用一次,或者重写父类的方法 而不想写继承的情况,或者实现接口。一句话,少写代码!
特性:
1,立即消失:创建匿名内部类的时候会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用
2:无法显式创建构造器,匿名内部类没有类名,无法定义构造器,通常只能用无参数构造器。但是可以通过继承父类的方法来使用带参数的构造器。或者使用初始化块来达到类似的目的。----------------这个李刚书上的 无法理解……不知道是不是写错了
3:如果匿名内外部的局部变量(只有局部变量)被匿名内部类访问了,那么这个变量相当于自动加了final修饰符(java8后)为什么后面会讲
实例:
直观印象,避免复杂的语句!
new Thread(){
public void run(){
System.out.println(getName());
}
}.start();
作者:雨杉
链接:https://www.zhihu.com/question/49330534/answer/212085553
用法1,当只使用一次对象时,减少多次使用语句
Pet pet=new Dog();
pet.eat;
//等同于
new Dog().eat();
作者:浠洛
链接:https://www.zhihu.com/question/49330534/answer/2253532128
用法2,重写父类方法,避免当只用一次的子类 还要写一堆定义····
person p = new person(){
@Override
public void method() {
System.out.println("重写父类方法");
}
};
p.method();
//其实可以进一步简化 如
new pet(){
@Override//重写父类方法
public void eat(){
systeam.out.println("正在吃......")
}.eat();
3、实现接口 特别点击事件用的多
Interface Product{
getPrice和getName方法
}
public void test(Product p);//方法定义
x.test(new Product(){ //传入的是实例 无实例名
里面实现接口要求的方法即可
})
比如
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
PS:
编译后的结构
这么记吧,所有内部类会在编译的时候产生相对应的class文件,非匿名内部类类名规则为 OutClass$InnerClass
(外部类类名与内部类类名中间用$连接) 匿名内部类类名则为OutClass$数字(OutClass$1,OutClass$2,OutClass$3)
情况1
//Test1.java
public class Test1 {
public static void main(String[] args) {
new Fu().method();
}
}
class Fu {
public void method() {
System.out.println("hhh");
}
}
//编译后变成两个文件Fu.class Test1.class
//Test1.class
public class Test1
{
public static void main(final String[] array) {
new Fu().method();
}
}
//Fu.class
class Fu
{
public void method() {
System.out.println("hhh");
}
}
情况2
//Test2.java
public class Test2 {
public static void main(String[] args) {
new Fu() {
@Override
public void method() {
System.out.println("hhhhhh");
}
}.method();
}
}
abstract class Fu {
public abstract void method();
}
//编译后变成三个文件
//Test2.class
public class Test2
{
public static void main(final String[] array) {
new Fu() {
@Override
public void method() {
System.out.println("hhhhhh");
}
}.method();
}
}
//Test2$1.class
class Test2$1 extends Fu {
@Override
public void method() {
System.out.println("hhhhhh");
}
}
//Fu.class
abstract class Fu
{
public abstract void method();
}
这种情况 $1的类与主类无关
我们就可以大胆的说,匿名内部类,其实就是匿名的子类而已,只不过你隐藏在.class文件里面你看不到,所以叫匿名内部类!
情况3
public class OuterClass {
public void display(final String name,String age){
class InnerClass{
void display(){
System.out.println(name);
}
}
}
}
从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下:
public class OuterClass$InnerClass {
public InnerClass(String name,String age){
this.InnerClass$name = name;
this.InnerClass$age = age;
}
public void display(){
System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );
}
}
首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用:
简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。(如果看不懂的话 先看情况4)
情况4 this.val$name
与为什么内部匿名类使用外部变量需要final修饰
一天偶尔在网上找到一个jar包,反编译后出现了如下的代码:
public void defineAnonymousInnerClass(String name){
new Thread(name) { //extra constructor argument "name"
public void run() {
System.out.println(this.val$name); //"this.val$" is extra
}
}.start();
}
当看到this.val$name
时,我就纠结了。这是什么东西还能这么使用?后来在百度上搜索答案,却始终不得答案。最终在google找到了答案。
Is simply setting the title of a JFrame object, using the value of the attribute
val$xxx
for doing so.val$xxx
is an instance attribute of the current class, its name is a bit unusual (because of the $) but nevertheless, valid for an identifier in Java.
其实,我是被编译器给玩了。这些代码都是编译器反编译的时候自己添加上去的。
第一个问题:匿名内部类要使用外面的参数,必须要加final。而代码中却没有加。
第二个问题:new Thread 这个类没有带参数的构造器。
第三个问题:this.val$
这种用法很怪异,val$
是哪里来的。
这些问题都是反编译器给我们搞的鬼。
我们在学习匿名内部类的时候都知道,
匿名内部类要使用外部的变量,或者参数,这个变量和参数都必须是final类型。
而且匿名内部类没有构造器他的构造器是继承于父类。
这就还有一个问题就是那我们如何初始化内名内部类中的变量,只需要使用“{}”在里面初始化变量不需要任何修饰。
再就是在匿名内部类中this表示当前对象,如果要使用外部类对象需要加上Outclass.this这才是外部对象。
所以以上代码我们做如下修改:
//参数加final
public void defineAnonymousInnerClass(final String name){
new Thread() { //额外的参数去掉
public void run() {
System.out.println(name); //"this.val$" 多余的去掉
}
}.start();
}
//其实很简单,这是由于反编译时没有给内部类中要使用的变量加final属性。只要去掉多余的this.val$ ,然后在定义的地方加上final属性即可。(若参数是形参 可能还要检查那啥)
这时我们就可以正常运行了,其实都是编译器搞的鬼。
为什么内部匿名类使用外部变量需要final修饰
因为生命周期的原因。方法中的局部变量,方法结束后这个变量就要释放掉,final保证这个变量始终指向一个对象。
首先,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。
注意:这个外部变量仅仅指局部变量,对全局变量无要求。
public void test() {
// 外部变量
final int i = 0;
new Thread() {
@Override
public void run() {
// 匿名内部类引用外部变量
// 此时这里的 i 其实是外面的 i 的副本
// 编译会报错: Cannot assign a value to final variable 'i'
i = 10;
}
};
}
————————————————
版权声明:本文为CSDN博主「艾斯比的日常」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012745499/article/details/112300679
问:什么 JDK8 之前内部类中如果有访问外部类的成员时,必须要加上 final?为什么 JDK8 不用了?
答:我们先不说 JDK 版本,就说说为什么要加 final。
我先给出问题的答案:用 final 修饰实际上就是为了保护数据的一致性。
这里所说的数据一致性,对引用变量来说是引用地址的一致性,对基本类型来说就是值的一致性。
这里我插一点,final 修饰符对变量来说,深层次的理解就是保障变量值的一致性。为什么这么说呢?因为引用类型变量其本质是存入的是一个引用地址,说白了还是一个值(可以理解为内存中的地址值)。用 final 修饰后,这个这个引用变量的地址值不能改变,所以这个引用变量就无法再指向其它对象了。
首先,因为生命周期的原因。方法中的局部变量,方法结束后这个变量就要释放掉,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁,它的生命周期同外部类相同。问题就来了,那么当外部类方法执行完毕的时候,这个局部变量肯定也就出栈了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。
为了解决这个问题,java 会将这个变量复制一份作为成员变量内置于内部类中,这样的话,及时外部类方法的局部变量出栈失效,我们也可以引用到复制的内部类中的成员,相当于延长了局部变量的 “生命”。 但是这也仅仅解决了局部变量的生命周期与局部内部类的对象的生命周期的不一致性问题。也是为什么局部变量要作为内部类构造方法的参数传入。
回到正题,为什么需要用 final 保护数据的一致性呢?
如果我们不用 final 修饰外部类方法局部变量,因为内部类(包括匿名内部类)对于局部变量的引用并不是直接的引用,而是将数据拷贝到自己的类变量中在加以使用,则局部变量可以发生变化。这里到了问题的核心了,如果局部变量发生变化后,匿名内部类是不知道的(因为他只是拷贝了局部变量的值,并不是直接使用的局部变量)。这里举个栗子:原先局部变量指向的是对象 A,在创建匿名内部类后,匿名内部类中的成员变量也指向 A 对象。但过了一段时间局部变量的值指向另外一个 B 对象,但此时匿名内部类中还是指向原先的 A 对象。那么程序再接着运行下去,可能就会导致程序运行的结果与预期不同。
在 JDK8 中如果我们在匿名内部类中需要访问局部变量,那么这个局部变量不需要用 final 修饰符修饰。看似是一种编译机制的改变,实际上就是一个语法糖(底层还是帮你加了 final)。但通过反编译没有看到底层为我们加上 final,但我们无法改变这个局部变量的引用值,如果改变就会编译报错。
为什么必须要为final呢?
首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用:
public class OuterClass {
public void display(final String name,String age){
class InnerClass{
void display(){
System.out.println(name);
}
}
}
}
从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下:
public class OuterClass$InnerClass {
public InnerClass(String name,String age){
this.InnerClass$name = name;
this.InnerClass$age = age;
}
public void display(){
System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );
}
}
简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。
发现jdk1.8必须加final,jdk13又不用加final,java语法糖会在底层自动加上,网络搜索得到1.9开始默认加上final。
情况5
public class MyChartItem {
private Stringx;
private float y;
private float lowTemp;
private StringweatherDayStr;
private StringweatherNightStr;
private int dayIcon;
private int nightIcon;
public MyChartItem(Stringvx,floatvy,floatlowT, StringdayStr, StringnightStr) {
this.x =vx;
this.y =vy;
this.lowTemp =lowT;
this.weatherDayStr =dayStr;
this.weatherNightStr =nightStr;
dayIcon = getWeatherIcon(dayStr,true);
nightIcon = getWeatherIcon(nightStr,false);
}
class forcastItem{
int wind;
int dayTemp;
int nightTemp;
int aqi;
public forcastItem(){
wind = 0;
dayTemp = 0;
nightTemp = 0;
aqi = 0;
}
public void setItem(){
y = dayTemp;
lowTemp = nightTemp;
String tempStr = x;
tempStr += "5/29";
}
}
}
/ Referenced classes of package com.miva.myWeather:
// MyChartItem
//有时http://www.showmycode.com 反编译出来的类名优点奇怪,例如我这里可能类名编程aqi……
class MyChartItem$forcastItem
{
public void setItem()
{
MyChartItem.access$0(MyChartItem.this, dayTemp);
MyChartItem.access$1(MyChartItem.this, nightTemp);
StringtempStr = MyChartItem.access$2(MyChartItem.this);
tempStr = (newStringBuilder(String.valueOf(tempStr))).append("5/29").toString();
}
int wind;
int dayTemp;
int nightTemp;
int aqi;
final MyChartItem this$0;
public ()
{
this$0 = MyChartItem.this;
super();
wind = 0;
dayTemp = 0;
nightTemp = 0;
aqi = 0;
}
}
通过上述定义,我们看到内部类在构造时,会被编译器自动传入外部类对象的一个引用,针对这里就是变量“final MyChartItem this$0;”;这样内部类中就可以访问外部类中的成员变量和函数;
“MyChartItem.access$0(MyChartItem.this)” 是对MyChartItem类中第一个成员变量“y”的引用;
Lambda表达式
Lambda支持将代码块作为方法参数,允许用更简介的代码来创建只有一个抽象方法的接口(函数式接口,但是可以有多个默认方法或者类方法)
首先介绍命令模式----将方法作为参数本身传入
//首先定义一个command接口
public interface Command{
void process(int[] array);
}
public class ProcessArray{
public void Process(int[] target ,Command command){
cmd.process(target);
}
}
mian(){
ProcessArray a = new ProcessArray();
a.process(target,command)//target是个数组,command是个类的对象 其定义不在这里写了
}
可以看到,上述例子要写很多代码,很麻烦,所以可以用内部匿名类改写
a.process(target,new Command(){
void process(int[] array){
int sum=0;
for(int tmp :target){
sum +=tmp;
}
syso(sum);//求和
}
})
但还是很麻烦,我们其实只要一个函数传入进去,但是这里为了面向对象,传入了一个类(更准确的说是实例)!
所以,用Lambda改写
a.process(target,(int[] target)->{
int sum=0;
for(int tmp :target){
sum +=tmp;
}
syso(sum);//求和
})
所以,Lambda表达式的主要作用就是代替匿名内部类的繁琐语法!
Lambda表达式由三部分组成
1、形参列表,形参列表允许省略形参类型,如果形参列表只有一个参数,甚至形参列表的圆括号也可省略。如果没有形参,那么一对圆括号即可。
2、箭头
3、代码块,如果只有一条语句,那么可以省略花括号;如果只有一条return语句,那么return关键字也可省略
Lambda表达式的目标类型必须是函数式接口(函数式接口可用@FunctionalInterface注解,让编译器去检查),Lambda只能实现一个方法,所以只能为函数式接口创建变量
为了保证Lambda表达式的目标类型是一个明确的函数式接口,可以用如下三种常见方式(到时候再看李刚的书)
冒号语法
Lambda的方法引用与构造器引用的阿双冒号快捷语法,见李刚P216
和匿名内部类的不同
1、匿名内部类可以为任意接口创建实例,但lambda只能为函数式接口创建实例
2、匿名内部类能为抽象类甚至普通类创建实例,Lambda只能函数式接口
其他
默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法,