#Java 核心技术卷一阅读笔记# 第六章 接口、lambda表达式与内部类

18 篇文章 0 订阅

6.1 接口(interface)

接口:用来描述类具有某种(些)功能,实现(implement)了接口的类必须要给出接口方法的实现。
接口中所有的方法都属于public,故在接口的定义中忽略不写,但在类中实现时需要写出。
接口中包含三种方法:1.抽象方法,必须要实现了该接口的类中给出实现;2.静态方法(static),在接口内实现方法;3.默认(default)方法,在接口内为此方法给出一个默认实现,必须用default修饰符来标记。这种方法可以让开发者只在类中实现(覆盖)自己感兴趣的方法即可,不必要将接口所有的方法都实现。而且在接口中添加新方法时,若将新方法设置为default,则可以实现“源代码兼用”。
接口与抽象类的区别
1.接口的实现使用implements+接口名,抽象类的继承使用extends+抽象类名
2.接口中不能包括实例域,只能包括上述的三种方法和常量。抽象类中可以包括抽象方法、具体方法实现、实例域、常量。
3.每个类只能由一个超类扩展而来,而能实现一个或多个接口(这就是为什么出现抽象类不能替代接口)

课外知识:强类型、弱类型、静态类型、动态类型语言
1.强/弱类型:弱/强类型指的是语言类型系统的类型检查的严格程度。比如弱类型偏向允许隐式类型转换。
2.静态/动态类型:静态类型指的是编译器在compile time执行类型检查,动态类型指的是编译器(虚拟机)在runtime执行类型检查。简单地说,在声明了一个变量之后,不能改变它的类型的语言,是静态语言;能够随时改变它的类型的语言,是动态语言。因为动态语言的特性,一般需要运行时虚拟机支持
Java是强类型静态语言。

让类实现一个接口:1.让类声明为实现给定的接口(Implement);2.对接口中的所有方法进行定义

接口不能实例化,但是能够创建接口变量,该变量必须引用实现了接口的类对象,可用instanceof来检测某对象是否实现了某接口(anObject instanceof 接口名),返回boolean值。接口也可以被扩展(继承),同抽象类(extends)

解决默认方法冲突:1.类优先;2.若是两个接口的方法产生了冲突:a)如果至少有一个接口提供了实现,则编译器则报告错误,让程序员解决这个二义性(在类中使用接口方法时在其之前加上“接口名.”);b)如果两个都没提供实现,则不存在冲突,可选择实现或不实现。

6.2接口示例

1.Comparator接口和Comparable接口
public int Comparable
{
int compareTo(Type b);
}
public int Comparator
{
int compare(Type a,Type b)
}
Comparable和Comparator都是用来实现集合中的排序的,Comparator位于包java.util下,而Comparable位于包java.lang下。Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作,已经实现了Comparable接口), Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。如String类默认是字典排序,而要想根据长度排序则要使用比较器。而用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。

import java.util.*;
import java.util.Comparator;

public class TimerTest
{
    public static void main(String[] args)
    {
        Person[] p = new Person[4];
        p[0] = new Person("ZXC",19);  
        p[1] = new Person("AFD", 109);  
        p[2] = new Person("AFD", 16);  
        p[3] = new Person("ERT",100); 
        Arrays.sort(p);//升序排列
        //Comparator<Person> cmp = new Cmp(); 建立一个接口对象,引用实现了接口的类的对象,用此对象传递接口的方法(因为Java是面向对象的语言)
        //Arrays.sort(p,new Cmp()); 降序排列
        System.out.println(Arrays.toString(p));
    }
}
class Person implements Comparable<Person>
{
    private String name;
    private int age;
    public Person(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    public String getName()
    {return name;}
    public int getAge()
    {return age;}
    public String toString()
    {return "Name:"+this.name+" Age:"+this.age;}
    public int compareTo(Person other)
    {
        if(this.getName().compareTo(other.getName()) != 0)
            return this.getName().compareTo(other.getName());
        else{
            if(this.getAge() < other.getAge())
                return -1;
            else if(this.getAge() > other.getAge())
                return 1;
            else return 0;
        }
    }
}
class Cmp implements Comparator<Person>//Arrays.sort()会采用compare()返回的整数来排序,默认是升序排列,如果想要换成倒序则将大于返回-1
{
    public int compare(Person a,Person b)
    {
        if(a.getName().compareTo(b.getName()) != 0)
            return a.getName().compareTo(b.getName());
        else{
            if(a.getAge() > b.getAge())
                return -1;
            else if(a.getAge() < b.getAge())
                return 1;
            else
                return 0;
        }   
    }
}

Comparator:负数排前,正数在后。
2.Cloneable接口
用于复制一个对象的状态,且复制后两者的状态不会相互影响。
所有的数组类型包含一个public的clone()方法,可用这个方法建立一个包含有所有元素的新副本

import java.util.Arrays;
public class CloneTest {
    public static void main(String[] args)
    {
        int[] nums = {1,2,3,4,5};
        int[] clones = nums.clone();
        nums[1] = 3;
        System.out.println(Arrays.toString(nums));
        System.out.println();
        System.out.println(Arrays.toString(clones));
    }
}

输出为:
[1, 3, 3, 4, 5]

[1, 2, 3, 4, 5]

Cloneable接口实际上是标记接口(tagging interface),它没有抽象方法,唯一作用是使用instanceof来表示一个类是否实现了该接口。clone()是从Object类继承而来,但是使用clone()的类必须要表明实现Cloneable接口(implements Cloneable)。
Cloneable是一个protected的方法,如果要让某类实现该接口,就需要在类中的clone()实现注明为public。
clone()可以分为浅复制和深复制:浅复制是指复制了对象中的原始数据类型,对于复制的对象类型,依旧是共享的,如果对象类型是不可变的(如String),则没有影响,如果是可变的,则需要进行深复制。

class Employee implements Cloneable
{
    public Employee clone() throw CloneNotSupportedException
    {
        //call object.clone()
        Employee clones = (Employee) super.clone();
        //clone mutable fields
        clones.hireDay = (Date) hireDay.clone()
        return clones; 
    }
}

6.3 lambda表达式

函数式接口(functional interface):只有一个抽象方法的接口。需要这种接口的对象时,可以提供Lambda表达式。
Java是一种面向对象的语言,如果要像方法传递代码块,则需要构造一个对象,而对象的类中需要有一个方法来包含需要传递的所有代码。
Lambda表达式是一个可传递的代码块,以及必须传入代码的变量规范(参数类型)。在以后可执行一次或者多次,用来取代匿名类(见后)。其形式为:参数,箭头(->),表达式/代码块,自由变量的值(非参数而且不在代码中定义的变量)。
不使用lambda表达式:

class lengthCompare implements Comparator<String>
{
    public int compare(String a,String b)
    {
        return a.length()-b/length();
    }
}
Comparator<String> cmp = new lengthCompare();
//Arrays.sort(T[] a,Comparator<T> c)
Arrays.sort(arr,cmp);

使用lambda表达式:(表达式无需制定返回类型)

Arrays.sort(arr,(String a,String b) -> a.length()-b.length());
//Arrays.sort方法会接受实现了Comparator<String>的某个类的对象,而在此对象调用compare方法会执行这个lambda表达式的体

当可以推导出lambda表达式的类型参数时,可省略参数的参数类型:

// Comparator<String> cmp = (a,b)->a.length()-b.length();
Arrays.sort(arr,(a,b)->a.length()-b.length());

当lambda表达式没有参数类型时,括号依然要保留:

()->{for(int i = 0;i < 10;i++) System.out.println(i);}

当lambda表达式只有一个参数且类型可以推导出来时,可省略括号和类型:

event -> System.out.println("The time is "+new Date());

当代码无法用表达式表述,则可以使用代码块,并包含显示的return语句:
注:每个分支都要有return

(String a,String b)->
{
    if(a.length() < b.length()) return -1;
    else if(a.length() > b.length()) return 1;
    else return 0;
}

方法引用:将现有的方法传递给某方法,用::分隔方法名和对象或类
1.object::instanceMethod:

Timer t = new Timer(1000,System.out::println);
//等同于Timer t = new Timer(1000,event->System.out.println(event));

2.Class::StaticMethod

Math::pow;
等同于(x,y)->Math.pow(x,y);

3.Class::instanceMethod

Arrays.sort(Strings,String::compareToIgnoreCase);
等同于Arrays.sort(Strings,(x,y)->x.compareTo(y));

也可以在方法引用中使用this/super
4.构造器引用
类的构造器的引用

ArrayList<String> names = ...;
Stream<Person> stream = names.stream().map(Person::new);
// Person::new是Person构造器的一个引用

数组的构造器引用

int[]::new 
等同于x->new int[x]

流库利用构造器引用构造泛型数组

Person[] p = stream.toArray(Person::new);

lambda表达式可以捕获外围方法或类中的变量,但是只能引用值不会改变的变量,而且此变量在外部也不能改变,即初始化后不会再赋予新值。
lambda表达式中的this参数是指创建这个表达式的方法的this参数

public class Application
{
    public void init()
    {
        ActionListener listener = event ->
        {
            System.out.println(this.toString());//会调用Appliction对象的toString()
        }
    }
    ...
}

6.4 内部类

1.即定义在一个类中的另一类,内部类不仅可以访问自身数据域,也可以访问创建它的外围类的数据域
内部类对象有一个隐式引用,指向创建它的外部类

public class TalkingClock//outer class
{
    private boolean beep;
    ....
    ...
    public class TimePrinter implements ActionListener
    // an inner class
    {
        public void actionPerformed(ActionEvent event)
        {
            System.out.println("Hello");
            if(beep) Toolkit.getDefaultToolKit().beep();//outer.beep
        }
    }
}

构造外部类时编译器给内部类构造了一个默认构造函数,传递参数为外部类,传递给对象变量。
2.内部类的语法规则
a.内部类引用外部类变量:OuterClass.this

public void actionPerformed(ActionEvent event)
{
    System.out.println("Hello");
    if(TalkingClock.this.deep) Toolkit.getDefaultToolKit().beep();
}

b.外部类中创建内部类:outerObject.new InnerClass(construction parameters)

ActionLister listener = this.new TimePrinter();

c.外部类作用域外引用内部类:OuterClass.InnerClass

TalkingClock j = new TalkingClock(1000,true);
TalkingClock.TimePrinter listener = j.new TimePrinter();

3.局部内部类:当在某方法中只是用某类一次,不能用public/private声明

public void start(int interval,boolean beep)
{
class TimePrinter implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {
            System.out.println("Hello");
            if(beep) Toolkit.getDefaultToolKit().beep();
        }
    }
ActionListener listener = new TimePrinter();
...
}

可以访问外部类和局部变量(即包含它的方法的实参),但是参数必须是final型的,不能改变
4.匿名内部类,即没有类名,只需要构建某类的对象一次

public void start(int interval,boolean beep)
{
    ActionListener listener = new ActionListener()//创建一个实现AL接口的类的对象
    {
        public void actionPerformed(ActionEvent event)
        {
            System.out.println("Hello");
            if(beep) Toolkit.getDefaultToolKit().beep();
        }
    };
...
}

“双括号语法”

invite(new ArrayList<String>{{add("Mary");add("Bill");}};

外括号创建ArrayList的匿名子类,内括号为对象构造块
5.静态内部类:把类隐藏在另一类的内部,内部类不需要访问外围类对象,则将内部类声明为static,可有静态域和方法。

//同时返回数组中的最大值和最小值
public class test {
    public static void main(String[] args)
    {
        double[] a = new double[20];
        for(int i = 0;i < 20;i ++)
            a[i] = Math.random() * 100;
        ArrayAlg.Pair p = ArrayAlg.minmax(a);
        System.out.println("Min = "+p.getfirst());
        System.out.println("Max = "+p.getsecond());
    }

}
class ArrayAlg//outer class
{
    public static class Pair//static inner class
    {
        private double first;
        private double second;
        public Pair(double first,double second)
        {
            this.first = first;
            this.second = second;
        }
        public double getfirst()
        {return first;}
        public double getsecond()
        {return second;}
    }
    public static Pair minmax(double[] values)
    {
        double max = Double.NEGATIVE_INFINITY;
        double min = Double.POSITIVE_INFINITY;
        for(double w : values)
        {
            if(w < min) min = w;
            if(w > max) max = w;
        }
        return new Pair(min,max);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值