日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

CoreJava 笔记总结-第六章 接口、lambda表达式与内部类

發布時間:2023/12/4 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CoreJava 笔记总结-第六章 接口、lambda表达式与内部类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 第六章 接口、lambda表達式與內部類
    • ==接口==
      • 接口的概念
      • 接口的屬性
      • 接口與抽象類
      • 靜態和私有方法
      • 默認方法
      • 解決默認方法沖突
      • 接口與回調
      • `Comparator`接口
      • 對象克隆
    • ==`lambda`表達式==
      • ==函數式接口==
      • 方法引用
      • 構造器引用
      • 變量作用域
      • 處理`lambda`表達式
      • 再談`Comparator`類
    • 內部類
      • 使用內部類訪問對象的狀態
      • 局部內部類
      • 由外部方法訪問變量
      • 匿名內部類
      • 靜態內部類
    • 代理
      • 何時使用代理
      • 創建代理對象
      • 代理的特性

第六章 接口、lambda表達式與內部類

  • 接口: 描述類應該做什么, 不指定如何做
  • lambda表達式: 表示使用回調或者可變行為的代碼

接口

接口的概念

  • 接口: 不是類, 而是對希望符合這個接口的類的一組需求

  • Arrays類中的sort方法對對象數組進行排序, 要求對象所屬的內必須實現Comparable接口

public interface Comparable {int compareTo(Object other); } //java5后 public interface Comparable<T> {int compareTo(T other); }
  • 接口中的所有方法自動為public
  • 接口中可以包含多個方法, 但是接口不會有實例字段
  • 讓一個類實現一個接口:
  • 將類聲明為實現給定的接口
  • 對接口中的所有方法提供定義
  • 將類聲明為實現為某個接口, 使用關鍵字implements
class Employee implements Comparable
  • Java API建議equals, compareTo方法兼容
  • 例外:x = BigDecimal("1.0"); y = BigDecimal("1.00"); x.equals(y)//false x.compareTo(y) == 0
package chapter6_interface_lambda_innerClass.interfaces;public class Employee implements Comparable<Employee>{private String name;private double salary;public Employee(String name, double salary) {this.name = name;this.salary = salary;}public double getSalary() {return salary;}public String getName() {return name;}public int compareTo(Employee other) {return Double.compare(salary, other.salary);} }package chapter6_interface_lambda_innerClass.interfaces;import java.util.Arrays;public class EmployeeSortTest {public static void main(String[] args) {var staff = new Employee[3];staff[0] = new Employee("Oukunnan", 25598);staff[1] = new Employee("Ovfdunnan", 18);staff[2] = new Employee("dsukunnan", 98);Arrays.sort(staff);for (Employee employee : staff) {System.out.println(employee.getName() + " salary=" + employee.getSalary());}} }

接口的屬性

  • 接口不是類, 不能用new運算符實例化一個接口
  • 能夠聲明接口變量: Comparable x ; //OK
  • 接口變量必須引用實現了這個接口的類對象 x = new Employee(...);
  • instanceof: 1. 檢查一個對象是否屬于某個特定的類 2. 一個對象是否實現了某個特定的接口
if (anObject instanceof Comparable){...};
  • 與建立類的繼承層次類似, 可以擴展接口
public interface Moveable{...} public interface Powered extends Moveable{..}
  • 接口不能包含實例字段但是可以包含常量
  • 接口的方法總是public, 接口的字段總是public static final, 都可以省略, 建議省略
  • 每個類只有一個超類, 卻可以實現多個接口, 有點像C++的多重繼承

接口與抽象類

  • 抽象類問題: 每個類只能擴展一個類
  • java可以擴展一個基類并且派生多個接口

靜態和私有方法

  • 標準庫中成對出現的接口和實用工具類: Collection/Collections, Path/paths
  • 允許在接口中增加靜態方法, 一般做法是放在伴隨類中
  • java9中接口中的方法可以是private

默認方法

  • 接口方法提供一個默認實現, default修飾符標記
public interface Comparable<T> {default int CompareTo(T other) {return 0;} }
  • 如果迭代器是只讀的就不用實現remove方法

    public interface Iterator<E> {boolean hasNext();E next();default void remove(){throw new UnsupprotedOperationException("remove");} }
  • 另一個作用是接口演化, 實現源代碼兼容


解決默認方法沖突

  • 超類優先
  • 同時實現的兩個接口中由完全同名并且參數類型相同的方法, 要求這個類實現該方法覆蓋接口的方法
  • class Student extends Person implements Named{...}只會考慮超類方法, 類優先原則

接口與回調

  • 回調: 指定某個特定事件發生時應該采取的動作
package chapter6_interface_lambda_innerClass.timer;import java.awt.event.*; import java.awt.*; import java.time.*; import javax.swing.*;public class TimerTest {public static void main(String[] args) {var listener = new TimePrinter();var timer = new Timer(1000, listener);//每隔1000ms(1s)響鈴并且打應輸出timer.start();JOptionPane.showMessageDialog(null, "Quit Program?");//展示消息框System.exit(0);} }class TimePrinter implements ActionListener {public void actionPerformed(ActionEvent event) {//ActionEvent 事件參數,提供事件的相關信息 //getWhen的得到紀元以來的毫秒數, 利用函數轉換成可讀時間System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));Toolkit.getDefaultToolkit().beep(); //調用默認工具箱響鈴} }

Comparator接口

  • 對于對象數組進行排序, 前提是這些對象是實現了Comparable接口類的實例
  • 如果按照長度而不是字典順序對于字符串進行排序, 使用Arrays.sort的另一種版本, 一個數組和比較器作為參數
  • 比較器實現Comparator接口
public interface Comparator<T> {int compare(T first, T second); }class LengthComparator implements Comparator<String> {public int compare(String first, String second){return first.length() - second.length();} }調用 Arrays.sort(words, new LengthComparator());

對象克隆

  • 拷貝: 一個包含對象引用的變量建立副本時,原變量和副本都是對同一個對象的引用, 任何一個對象的引用都會改變另一個變量
  • 克隆: 希望變量是一個新的對象, 初始狀態和原變量相同, 之后會有各自不同的狀態
  • 默認的克隆操作是淺拷貝: 逐個字段拷貝, 對于數值和其他基本類型克隆, 但是對于包含對象引用的子對象也會共享一些信息
  • 如果原對象和淺克隆對象共享的子對象是不可變的, 那么淺拷貝的共享安全
  • 深拷貝: 子對象可變的, 必須重新定義clone方法建立深拷貝, 克隆所有對象
  • Cloneable: 標記接口, 不包含任何方法(一般的接口確保一個類實現一組特定的方法), 作用:允許類型查詢中使用instanceof
if(obj instanceof Cloneable) ...
  • 所有數組類型都有一個公共的clone方法, 不是受保護的
int[] a = {1, 2, 3}; int[] b = a.clone(); b[0] = 5;// a[0] == 1; package chapter6_interface_lambda_innerClass.clone;public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException{var original = new Employee("Ou Kunnan", 52000);original.setHireDay(2001, 12, 26);Employee copy = original.clone();System.out.println(copy); // 自動調用toString方法,相當于copy.toString()copy.raiseSalary(10);copy.setHireDay(2002, 12, 26);System.out.println("original: " + original);System.out.println("copy: " + copy);} }package chapter6_interface_lambda_innerClass.clone;import java.util.Date; import java.util.GregorianCalendar;public class Employee implements Cloneable{private double salary;private String name;private Date hireDay;public Employee(String name, double salary) {this.name = name;this.salary = salary;hireDay = new Date();}public Employee clone() throws CloneNotSupportedException {Employee cloned = (Employee) super.clone();cloned.hireDay = (Date) hireDay.clone();return cloned;}public void setHireDay(int year, int month, int day) {Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();hireDay.setTime(newHireDay.getTime());}public void raiseSalary(double byPercent) {double raise = salary * byPercent / 100;salary += raise;}public String toString() {return "Employee[name=" + name + ", salary=" + salary + ", hireDay=" + hireDay + "]";} }

lambda表達式

  • lambda表達式就是一個代碼塊,以及必須傳入的代碼變量規范

  • 形式: 參數, ->, 一個表達式

  • (String first, String second)->{if(first.length() < second.length()) return -1;else return 1; }
  • lambda沒有參數,`()-> {…};

  • 如果可以推導出lambda表達式參數類型, 可以忽略其類型

Comparator<String> comp = (first, second) -> first.length() - second.length();
  • 只有一個參數并且類型可以推到, 可以省略小括號

  • 無需指定返回類型

package chapter6_interface_lambda_innerClass.lambda;import java.util.*;import javax.swing.*; import javax.swing.Timer;public class lambdaTest {public static void main(String[] args) {var planets = new String[]{"Mercury", "Venus", "Earth", "Mars","Jupiter", "Saturn", "Uranus", "Neptune"};System.out.println(Arrays.toString(planets));System.out.println("Sorted in dictionary order:");Arrays.sort(planets);System.out.println(Arrays.toString(planets));System.out.println("Sorted by length:");Arrays.sort(planets, (first, second) -> first.length() - second.length());//第二個參數應該是比較器System.out.println(Arrays.toString(planets));var timer = new Timer(1000, event -> System.out.println("The time is " + new Date()));timer.start();JOptionPane.showMessageDialog(null, "quit?");System.exit(0);} }

函數式接口

  • 函數式接口: 對于只有一個抽象方法的接口,需要這種接口對象時, 可以提供一個lambda表達式
  • lambda表達式可以轉換為接口
var timer = new Timer(1000, event->{System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));Toolkit.getDefaultToolkit().beep(); })
  • ArrayList類的removeIf方法參數是Predicate
public interface Predicate<T> {boolean test(T t); } //刪除列表所有null值 list.removeIf(e -> e == null);
  • supplier沒有參數, 調用時會生成T類型的值, 用于實現懶計算
public interface Supplier<T> {T get(); }LocalDate hireDay = Objects.requireNonNullOrElse(day, new LocalDate(1970, 1, 1));//通過使用供應者supplier, 延遲該計算 , 只有day==null時候才調用供應者(構造默認的LocalDate) LocalDate hireDay = Objects.requireNonNullOrElse(day, () -> new LocalDate(1970, 1, 1));

方法引用

  • var timer = new Timer(1000, event->System.out.println(event)); var timer = new Timer(1000, System.out::println);
  • System.out::println是一個方法引用, 它指示編譯器生成一個函數式接口的實例,覆蓋這個接口的抽象方法來調用給定的方法

  • 上面的例子, 會生成一個ActionListener, 他的actionPerformed(ActionEvent e)方法要調用System.out.println(e)

  • 方法引用不是對象, 為一個類型為函數式接口的變量賦值時會生成一個對象

  • 方法引用示例與等價的lambda表達式見P248

  • 當lambda表達式的體只調用一個方法而不做其他操作的時候才能把方法引用重寫為方法引用


構造器引用

  • Person::new就是Person構造器的一個引用
  • int[]::new是一個構造器引用, 他有一個參數,即數組的長度, 等價于x->new int[x]

變量作用域

  • lambda表達式三個部分: 代碼塊, 參數, 自由變量的值
  • 可以把一個lambda表達式轉換為一個包含方法的對象, 自由變量的值會復制到這個對象的實例變量中
  • lambda表達式是閉包的
  • lambda表達式可以捕獲外圍作用域中變量的值, 確保值是明確定義的(事實最終變量, 初始化后不會改變)

處理lambda表達式

  • lambda表達式重點是延遲執行

  • Runnable作為無參數或返回值的動作運行, action.run()會調用lambda表達式主體

    package test;import java.util.function.IntConsumer;public class lambda {public static void main(String[] args){repeat(10, ()->System.out.println("hello, world"));repeat(10, (i)->System.out.println("Countdown:" + (9-i)));}public static void repeat(int n, Runnable action){for(int i = 0; i < n; i++) action.run();}public static void repeat(int n, IntConsumer action) {for(int i = 0; i < n; i++)action.accept(i);} }

再談Comparator類

  • P255

內部類

  • 定義在另一個類中的類
  • 內部類可以對同一個包中的其他類隱藏, 內部類方法可以訪問定義這個類的作用域中的數據,包括原本的私有數據

使用內部類訪問對象的狀態

  • 一個內部類方法可以訪問自身的數據字段,也可以訪問創建它的外圍類對象的數據字段
  • 內部類對象總有一個隱式引用指向創建它的外部類對象
  • 這個引用在構造器中設置, 編譯器會修改所有內部類的構造器,添加一個對應外圍類引用的參數
以下這個例子生成的無參數構造器如下 public TimePrinter(TalkingClock clock){outer = clock;} package chapter6_interface_lambda_innerClass.innerClass; import java.awt.*; import java.awt.event.*; import java.time.*;import javax.swing.*;public class innerClassTest {public static void main(String[] args) {var clock = new TalkingClock(1000, true);clock.start();JOptionPane.showMessageDialog(null, "Quit?");System.exit(0);} }class TalkingClock{private int interval;private boolean beep;public TalkingClock(int interval, boolean beep) {this.interval = interval;this.beep = beep;}public void start() {var listener = new TimerPrinter();var timer = new Timer(interval, listener);timer.start();}public class TimerPrinter implements ActionListener {public void actionPerformed(ActionEvent event) {System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));if (beep) {Toolkit.getDefaultToolkit().beep();}}}}

局部內部類

  • TimePrinter名字只出現了一次,在start方法中創建這個類型對象時使用了一次.可以在一個方法中局部定義這個類

    public void start() {class TimerPrinter implements ActionListener {public void actionPerformed(ActionEvent event) {System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));if (beep) {Toolkit.getDefaultToolkit().beep();}}}var listener = new TimerPrinter();var timer = new Timer(interval, listener);timer.start(); }
  • 局部內部類聲明時候不能有訪問修飾符public, private

  • 優勢: 對外部完全隱藏,除了start代碼


由外部方法訪問變量

  • 局部內部類不僅能夠訪問外部類字段,還可以訪問局部變量(事實最終變量)

  • public void start(int interval, boolean beep) {class TimerPrinter implements ActionListener {public void actionPerformed(ActionEvent event) {System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));if (beep) {Toolkit.getDefaultToolkit().beep();}}}var listener = new TimerPrinter();var timer = new Timer(interval, listener);timer.start(); }

匿名內部類

  • 匿名內部類不需要為類指定名字

  • 以下代碼: 創建了一個類的新對象,這個類實現了ActionListener接口, 需要實現的方法{}中定義

    public void start(int interval, boolean beep) {var listener = new ActionListener();{public void actionPerformed(ActionEvent event) {System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));if (beep) {Toolkit.getDefaultToolkit().beep();}}};var timer = new Timer(interval, listener);timer.start(); }//用lambda表達式 public void start(int interval boolean beep) {var timer = new Timer(interval, event->{System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));if (beep) Toolkit.getDefaultToolkit().beep();}) }
  • 語法如下

new SuperType(construction parameters) {innner class methods and data }

SuperType可以是接口,內部類就要實現這個接口;如果是一個類,內部類就要擴展這個類

  • 構造器名字必須和類名相同,匿名內部類沒有類名所以沒有構造器
  • 構造參數要傳遞給超類構造器
  • 注意: 構造一個類的新對象和構造一個擴展了那個類的匿名內部類的對象之間的差別
vae queen = new Person("Marry"); var count = new Person("bjcs"){...};
  • 匿名內部類不能有構造器但是可以提供一個對象的

  • 雙括號初始化:

  • var f = new ArrayList<String>(); f.add("Harry"); f.add("Alice"); invite(f); ---> invite(new ArrayList<String>)(){{add("Harry"); add("Alice");}} //外層括號建立了一個匿名子類,內層括號是一個初始化塊
  • 得到匿名內部類的外部類類名不能直接getClass,這個方法帶調用this.getClass(), 靜態方法沒有隱式參數

new Object(){}.getClass().getEnclosingClass()

new Object()建立Object的匿名子類的一個匿名對象,getEnclosingClass則得到其外圍類,也就是包含這個靜態方法的類


靜態內部類

  • 只要內部類不需要訪問外圍類對象,就應該使用靜態內部類
  • 接口中聲明的內部類自動為public, static
package chapter6_interface_lambda_innerClass.staticInnerClass;public class StaticInnerClassTest {public static void main(String[] args){var values = new double[20];for (int i = 0; i < values.length; i++)values[i] = 100 * Math.random();ArrayAlg.Pair p = ArrayAlg.minmax(values);System.out.println("min = " + p.getFirst());System.out.println("max = " + p.getSecond());} }class ArrayAlg{public static class Pair {/* 一個靜態的內部類 */private double first;private double second;/*** Constructs a pair from two floating-point numbers** @param f the first number* @param s the second number*/public Pair(double f, double s) {first = f;second = s;}/*** Returns the first number of the pair** @return the first number*/public double getFirst() {return first;}/*** Returns the second number of the pair** @return the second number*/public double getSecond() {return second;}}/*** Computes both the minimum and the maximum of an array* @param values an array of floating-point numbers* @return a pair whose first element is the minimum and whose second element* is the maximum*/public static Pair minmax(double[] values){double min = Double.POSITIVE_INFINITY;double max = Double.NEGATIVE_INFINITY;for (double v : values) {if (min > v) {min = v;}if (max < v) {max = v;}}return new Pair(min, max);} }

代理

何時使用代理

  • 代理類在運行時闖將全新的類,這樣代理類可以實現你指定的接口
  • 代理類包含的方法: 指定接口所需要的全部方法, Object類中的全部方法(equals, toStirng等)

創建代理對象

  • 需要使用Proxy類的newProxyInstance方法, 有三個參數
    • 一個類加載器(這里指定系統類加載器)
    • 一個Class對象數組,每個元素對應需要實現的各個接口
    • 一個調用處理器
  • 代理作用: 將方法調用路由到遠程服務器;在運行中的程序將用戶界面事件與動作關聯起來;為了調試,跟蹤方法使用
package chapter6_interface_lambda_innerClass.proxy;import java.lang.reflect.*; import java.util.*;public class ProxyTest {public static void main(String[] args) {var elements = new Object[1000];for (int i = 0; i < elements.length; i++) {Integer value = i + 1;var handler = new TraceHandler(value);Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Comparable.class}, handler);elements[i] = proxy;}Integer key = new Random().nextInt(elements.length) + 1;int result = Arrays.binarySearch(elements, key);if(result >= 0) System.out.println(elements[result]);} }class TraceHandler implements InvocationHandler {//打應輸出方法名和參數,并且調用該方法private Object target;public TraceHandler(Object t) {target = t;}public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {System.out.print(target);System.out.print("." + m.getName() + "(");if (args != null) {for (int i = 0; i < args.length; i++) {System.out.print(args[i]);if(i < args.length - 1) System.out.print(", ");}}System.out.println(")");return m.invoke(target, args);} }

代理的特性

  • 代理是在運行過程中創建的,一旦創建就變成了常規類
  • 代理類都是擴展Proxy, 一個代理類只有一個實例字段即調用處理器,在超類Proxy中定義
  • 所有的代理類都要覆蓋toString, hasCode, equals方法, 這些方法只是在調用處理器上調用invoke.

總結

以上是生活随笔為你收集整理的CoreJava 笔记总结-第六章 接口、lambda表达式与内部类的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。