Java 笔记
接口
接口 (interface)定义了一种可以被类层次中任何类实现行为的协议,是常量、抽象方法、默认方法和静态方法的集合。接口可以用来实现多重继承。
接口被看作是一种特殊的类型。与常规类一样,每个接口都被编译为独立的字节码文件。使用接口与使用抽象类相似。接口可以作为引用变量的数据类型或类型转换的结果等。与抽象类一样,不能用 new运算符创建接口的实例.
接口中的抽象方法只有声明,没有实现。抽象方法也可以省略修饰符,省略修饰符编译器自动加上
public abstract
接口通常表示某种能力,因此接口名后缀通常是 able
1 2 3 4 5 6 7 8 9 10 [public ] interface InterfaceName [extends SuperInterfaces ]{ } [public ] class ClassName implements InterfaceList { }
如果实现接口的类不是 abstract类,则在类的定义部分必须实现接口中的所有抽象方法,即必须保证非 abstract类中不能存在 abstract方法。
接口方法的访问修饰符都是 public, 所以类在实现方法时,必须显式使用 public 修饰符,否则编译器警告缩小了访问控制范围.
定义在接口中的变量都自动加上public、final、static属性,因此它们都是常量,
常量的定义可以省略修饰符,下面三行代码效果相同.
int STATUS = 100;
public int STATUS = 100;
public final static int STATUS = 100;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 interface capable { void work () ; } interface thinkable { void desire () ; } class Person implements capable , thinkable { public String capability, desire; public void work () { System.out.println("I am capable of " + capability); } public void desire () { System.out.println("I wanna " + desire); } Person (String capability, String desire) { this .desire = desire; this .capability = capability; } public Person () {} } public class Main { public static void main (String[] args) { Person slacr = new Person ("writing Java" , "hava a rest" ); slacr.work(); slacr.desire(); } }
默认方法与接口继承
与类的继承类似,子接口继承父接口中的常量、抽象方法、默认方法, 除了静态方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 interface int01 { int STATUS = 0 ; void fn_1 () ; default void test () { System.out.println("default method from int01" ); } } interface int02 { default void test () { System.out.println("default method from int02" ); } } interface int03 extends int01 , int02 { @Override default void test () { int01.super .test(); } } class int04 { public void test () { System.out.println("test in class" ); } } class int05 extends int04 implements int02 { public void fn5 () { test(); } } class Switch implements int01 , int03, int02 { public void fn_1 () { System.out.println("fn_1" ); } } public class Main { public static void main (String[] args) { Switch s = new Switch (); System.out.println(s.STATUS); s.fn_1(); s.test(); } }
接口是特殊的抽象类, 接口只能继承接口, 类只能实现接口(某种程度上的继承).
接口类型使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 interface A { int a = 1 ; static void test () { System.out.println("test static interface method" ); } } interface B { int b = 2 ; } interface C extends A ,B { int c = 3 ; } class D implements A , B, C{ int d = 4 ; } public class Main { public static void main (String[] args) { A a = new D (); B b = new D (); C c = new D (); D d = new D (); System.out.println(a.a); System.out.println(b.b); System.out.println(c.a + " " + c.b + " " + c.c); System.out.println(d.a + " " + d.b + " " + d.c + " " + d.d); A.test(); } }
Comparable接口
Java类库中也定义了许多接口,有些接口中没有定义任何方法,这些接口称为标识接口,如java.lang包中定义的 Cloneable接口、java.io包中的 Serializable接口。有些接口中定义了若干方法,如java.lang包中Comparable接口中定义的comapreTo()方法、AutoClosable接口定义的 close()方法、Runnable 接口中定义的 run()方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Circle implements Comparable <Circle>{ public double r; public double area; public Circle (double r) { this .r = r; this .area = Math.PI*r*r; } @Override public int compareTo (Circle c) { return Double.compare(this .area, c.area); } } public class Main { public static void main (String[] args) { Circle c1 = new Circle (1 ); Circle c2 = new Circle (2 ); Circle c11 = new Circle (1 ); System.out.println(c1.compareTo(c2)); System.out.println(c2.compareTo(c1)); System.out.println(c1.compareTo(c11)); } }
JavaAPI中许多类实现了 Comparable接口,如基本数据类型包装类 (Byte、Short、Integer、Long、Float、Double、Character、Boolean)。File类、String类、LocalDate 类、BigInteger类和 BigDecimal类也实现了 Comparable接口,这些类的对象都可按自然顺序排序.
Comparator 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Circle { public double r; public double area; public Circle (double r) { this .r = r; this .area = Math.PI*r*r; } public String toString () { return String.format( "%.2f" , this .area) + " " ; } } class CircleComparator implements Comparator <Circle>{ public int compare (Circle l, Circle r) { return (int )(r.area - l.area); } } public class Main { public static void main (String[] args) { Circle[] circles = new Circle [5 ]; for (int i = 0 ; i < circles.length; i++) { circles[i] = new Circle (i); } Arrays.sort(circles, new CircleComparator ()); for (Circle circle : circles) { System.out.print(circle); } } }
Lambda表达式
Lambda表达式是Java SE 8 新增的一个语言特征。它将 Java的面向对象编程范式与函数式编程结合起来,可以增强Java在并发编程和事件驱动编程中的优势。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Main { public static void main (String[] args) { String[] names = {"andy" , "bible" , "candy" , "david" }; Arrays.sort(names, new Comparator <String>() { @Override public int compare (String o1, String o2) { return o2.compareTo(o1); } }); for (String name : names) { System.out.print(name + " " ); } System.out.println(); String[] names_2 = {"andy" , "bible" , "candy" , "david" }; Arrays.sort(names_2, (String a, String b) -> {return b.compareTo(a);}); for (String name : names_2) { System.out.print(name + " " ); } } }
函数式接口
函数式接口 (function interface)是指仅包含一个抽象方法的接口,因此也称为单抽象方法 (single abstract method,SAM) 接口。每一个Lambda 表达式都对应一个函数式接口类型。可以将 Lambda 表达式看作实现函数式接口的类的一个实例。默认方法不是抽象方法,所以在函数式接口中可以定义默认方法。
可以根据需要定义函数式接口,只要接口只包含一个抽象方法即可。在定义函数式接口时可以给接口添加@FunctionalInterface注解,如果接口定义多于一个的抽象方法,编译器会报错.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 interface Converter <F, T> { T convert (F from) ; default void convertInfo () { System.out.println(this .getClass()); } } public class Main { public static void main (String[] args) { Converter<String, Integer> converter = (from -> Integer.valueOf(from)); Converter<String, Integer> converter1 = new Converter <String, Integer>() { @Override public Integer convert (String from) { return Integer.valueOf(from); } }; Integer converted = converter.convert("382938" ); Integer converted1 = converter1.convert("21234321" ); System.out.println(converted); System.out.println(converted1); converter.convertInfo(); } }
在Java API中有些接口就只含有一个抽象方法,如 Runnable 接口、AutoCloseable 接口、Comparable 接口和 Comparator 接口等。此外,java.util.function中包含几十个函数式接口。函数式接口之所以重要是因为可以使用 Lambda 表达式创建一个与匿名内部类等价的对象。
预定义的函数式接口
在java.util.function包中定义了大量的函数式接口,它们使编写Lambda表达式变得容易。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import java.util.Random;import java.util.function.*;public class Main { public static void main (String[] args) { Function<Integer, Double> getPerimeter = (Integer r) -> {return 2 *Math.PI*r;}; System.out.println(getPerimeter.apply(2 )); BiFunction<Float, Float, Float> calcArea = (w, l) -> w*l; System.out.println(calcArea.apply(3f , 5f )); UnaryOperator<Integer> getfactorial = (Integer i) -> { int res = 1 ; for (int j = 2 ; j <= i; j++) { res*=j; } return res; }; System.out.println(getfactorial.apply(5 )); Predicate<String> isAllDigital = (String s) -> { for (int i = 0 ; i < s.length(); i++) { if (!Character.isDigit(s.charAt(i))) { return false ; } } return true ; }; System.out.println(isAllDigital.test("12345" ) + " " + isAllDigital.test("123ad22" )); Supplier<Integer> randomDigit = () -> new Random ().nextInt(10 ); System.out.println(randomDigit.get()); Consumer<char []> printcharArr = (chars -> { for (int i = 0 ; i < chars.length; i++) { System.out.print(chars[i]); } }); char [] chars = ("goodbye world" ).toCharArray(); printcharArr.accept(chars); } }
方法引用与构造方法引用
Java中有许多方法带一个函数式接口对象作为参数。如果传递的表达式有实现的方法,可以使用一种特殊的语法,方法引用 (method referencing) 代替Lambda 表达式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Main { public static void main (String[] args) { ArrayList<Integer> AL = new ArrayList <>(5 ); AL.add(1 ); AL.add(2 ); AL.add(3 ); AL.add(4 ); AL.forEach(new Consumer <Integer>() { @Override public void accept (Integer integer) { System.out.println(integer+1 ); } }); AL.forEach( i -> System.out.println(i+2 )); AL.forEach(System.out::println); String arr[] = {"Ali" , "Baidu" , "Tencent" , "ByteDance" }; Arrays.sort(arr, String::compareToIgnoreCase); for (int i = 0 ; i < arr.length; i++) { System.out.print(arr[i] + " " ); } System.out.println(); } }
方法引用是类名或对象引用,后跟:: 然后是方法名.
双冒号(:)是Java SE8 引进的一种新运算符,可以引用静态方法、实例方法甚至构造方法。方法引用有以下三种使用方式:
对象::实例方法名
类名::静态方法名
类名::实例方法名
使用第一种方式,在对象上调用实例方法,将给定的参数传递给实例方法,因此,System.out::println 等同于 x->System.out.println(x)
使用第二种方式,用类名调用静态方法,将给定的参数传递给静态方法。例如,java.util.Objects类定义了 isNull()静态方法,调用 Objects.isNull(x)直接返回x=null 的值。使用 list.removeif(Objects::isNull)将从列表中删除所有的 null值。
使用第三种方式,用类名调用实例方法,第一个参数作为方法的调用者,其他参数传递给方法。例如, String::compareToIgnoreCase 等同于(x,y)->x.compareToIgnoreCase(y)
构造方法引用中需要使用 new 运算符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class 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 class Main { public static void main (String[] args) { BiFunction<String, Integer, Person> personFactory = Person::new ; Person person = personFactory.apply("John Doe" , 30 ); System.out.println("Name: " + person.getName()); System.out.println("Age: " + person.getAge()); } }
可以使用数组类型编写构造方法引用。例如, int[]::new 是一个含有一个参数的构造方法引用,该参数为数组长度。它等同于 Lambda 表达式 n->new int[n]。
《Java程序设计(第3版)》 IBSN 9787302485520
Java API 文档