Java核心技术卷一 4. java接口、lambda、内部类和代理

接口

接口概念

接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。

如果类遵从某个特定接口,那么久履行这项服务。

public interface Comparable<T>{
    int compareTo(T other);
}

任何实现 Comparable 接口的类都需要包含 compareTo 方法,并且这个方法的参数必须是一个 T 对象,返回一个整形数值。

接口的特点

  • 接口中所有方法自动地属于 public,所以接口的方法不需要提供关键字 public 。
  • 接口中可以定义常量。更多请看接口的特性

接口不能提供的功能

  • 不能含有实例域
  • 不能在接口中实现方法(java 8 之前)

接口与抽象类的区别

  • 可以将接口看成是没有实例域的抽象类,但还是有一定区别。

让类实现一个接口的步骤

  1. 将类声明为实现给定的接口。
  2. 对接口中的所有方法进行定义。
class Employee implements Comparable<Employee> {
    public int compareTo(Employee other){
        return Double.compare(salary, other.salary);
    }
    ...
}

实现类中的特点

  • 实现方法的方法声明为 public ,因为接口中的方法都自动地是 public。
  • 为泛型 Comparable 接口提供一个类型参数,就可以不使用 Object 类型,使得程序省略了强制装换的步骤。

当使用 Array.sort() 方法时,必须实现 Comparable 接口方法,并且元素之间必须是可比较的,不然会报异常:

public class ArrayGood {
    public static void main(String[] args) {
        int[] a = Arrays.copyOf(new int[2], 100);
        System.out.println(a.length);//100

        Employee[] employees = new Employee[10];
        employees = Arrays.copyOf(employees, 100);

        int[] aint = {5, 3, 6, 14, 9, 7, 22, 10};
        System.out.println(Arrays.toString(aint));
        Arrays.sort(aint);
        System.out.println(Arrays.toString(aint));

        Employee[] employees1 = {new Employee("n")
                , new Employee("h")
                , new Employee("e")
                , new Employee("n")
                , new Employee("a")
                , new Employee("r")
                , new Employee("n")
                , new Employee("i")
                , new Employee("m")};
        Arrays.sort(employees1);//java.lang.ClassCastException: Employee cannot be cast to java.lang.Comparable
    }
}

public class Employee {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    Employee(String name){
        this.name = name;
    }

}

实现接口并实现方法后,Employee 数组调用了排序 sort() 方法,实现了排序:

public class ArrayGood {
    public static void main(String[] args) {
        int[] a = Arrays.copyOf(new int[2], 100);
        System.out.println(a.length);//100

        Employee[] employees = new Employee[10];
        employees = Arrays.copyOf(employees, 100);

        int[] aint = {5, 3, 6, 14, 9, 7, 22, 10};
        System.out.println(Arrays.toString(aint));
        Arrays.sort(aint);
        System.out.println(Arrays.toString(aint));

        Employee[] employees1 = {new Employee("n")
                , new Employee("h")
                , new Employee("e")
                , new Employee("n")
                , new Employee("a")
                , new Employee("r")
                , new Employee("n")
                , new Employee("i")
                , new Employee("m")};
        Arrays.sort(employees1);
        for (Employee emp:
             employees1 ) {
            System.out.print(emp.getName() + " ");
        }
        //a e h i m n n n r
    }
}

public class Employee implements Comparable<Employee>{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    Employee(String name){
        this.name = name;
    }

    @Override
    public int compareTo(Employee o) {
        return name.compareTo(o.name);
        //return Double.compare(double x, double y);
        //return Integer.compare(int x, int y);
    }
}

所以,要让一个类使用排序服务必须让它实现 compareTo 方法。这是理所当然的,因为要向 sort 方法提供对象的比较方法。

疑问:为什么不能在 Employee 类直接提供一个 compareTo 方法,而必须实现 Comparable 接口呢?

解答:原因在于 Java 程序设计语言是一种强类型语言。在调用方法的时候,编译器将会检查这个方法是否存在。调用 compareTo 方法时,sort 传入的 Object 对象会被强制转换为 Comparable 类型,因为只有一个 Comparable 对象才确保有 compareTo 方法。又因为存在这个强制转换,所以类必须还实现 Comparable 接口,这样才可以将 Object 引用的参数转化为一个 Comparable。

反对称规则:如果子类之间的比较含义不一样,那就属于不同类对象的非法比较。每个 compareTo 方法都应该在开始时进行下列检测

if (getClass() != other.getClass()) throw new ClassCastException();

接口的特性

  • 接口不是类,不能使用new实例化
  • 可以声明接口的变量
  • 接口变量必须引用实现了接口的类对象,可以使用instance检查一个对象是否实现了某个特定的接口

java x = new Comparable(...);//ERROR Comparable x;//OK x = new Employee(...)//Employee实现了接口 if(anObject instanceof Comparable) {...}

  • 如果类不实习接口的方法,那么这个类就要定义为抽象类

```java

public interface Named {

String getName();

}

abstract class Student implements Named{

}

```

  • 接口可以被扩展,并且能用 extends 扩展多个接口

```

public interface Moveable{

void move(double x, double y);

}

public interface Powered extends Moveable, Comparable

  • 接口不能包含实例域或静态方法,但可以包含常量,接口中的域自动设为public static final

public interface Powered extends Moveable{ double milesPerGallon(); double SPEED_LIMIT = 95; }

  • 接口只能继承一个超类,却可以实现多个接口

class Employee implements Cloneable, Comparable{ ... }

接口与抽象类

疑问:为什么引用接口概念,为什么不将 Comparable 直接设计为抽象类。

解答:如果使用抽象类表示通用属性存在一个问题,每个类只能扩展一个类。每个类却可以实现多个接口。

没有多重继承:许多设计语言允许一个类有多个超类,如 C++。而 Java 没有多继承是因为它会让语言本身变得非常复杂,降低效率。

静态方法

Java SE 8 中,允许在接口中增加静态方法。这有违接口作为抽象规范的初衷。

目前的方法(2018)都是将静态方法放在伴随类中。标准库中的接口和工具类,可能只包含一些工厂方法。

如 Path 接口定义了 Paths 类中的工厂方法,这样一来,Paths 类就不再是必要的了。

默认方法

Java SE 8 中可以为接口方法提供一个默认实现。必须用 default 修饰符标记:

public interface Comparable<T> {
    default int compareTo(T oter){
        return 0;
    }
}

一般情况没有用处,因为方法会被覆盖。

但有时一个接口定义了大量的方法,我们又不需要实现这么多方法,只关心其中一两个方法。在 Java SE 8 中,可以把所有方法声明为默认方法,这些默认方法声明也不做。

public interface MouseListener{
    default void moseClicked(MouseEvent event){}
    default void mosePressed(MouseEvent event){}
    default void moseReleased(MouseEvent event){}
    default void moseEntered(MouseEvent event){}
    default void moseExited(MouseEvent event){}
}

默认方法可以调用任何其他方法:

public interface Collection{
    int size();
    default boolean isEmpty(){
        retrun size()==0;
    }
    ...
}

public class Test implements Collection{
    public static void main(String[] args) {
        Test test = new Test();
        System.out.println(test.size());//0
        System.out.println(test.isEmpty());//true
    }

    @Override
    public int size() {
        return 0;
    }
}

这样实现 Collection 的程序员就不用操心实现isEmpty方法了。

解决默认方法冲突

如果接口中定义了默认方法,然后又在超类或另一个接口中定义了同样的方法,规则如下:

  1. 超类优先。如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略。
  2. 接口冲突。如果一个超接口提供了一个默认方法,另一个接口提供了自个同名而且参数类型相同的方法,必须覆盖这个方法来解决冲突。

来看看接口冲突的场景:

interface Named{
    default String getName() {
        return getClass().getName() + "_" + hashCode();
    }
    ...
}

interface Person {
    default String getName() {
        return getClass().getName() + "_" + hashCode();
    }
    ...
}

class Student implements Person, Named {
    public String getName() {
        return Person.super.getName();
    }
}

当 Student 继承两个接口时,Java 编译器会报告一个错误,让程序员重写有冲突的方法解决二义性。我们使用接口类型.super.接口方法的方法,在两个接口中选择一个方法,解决二义性。

假设 Named 接口没有为 getName 提供默认实现:

interface Named{
    String getName();
}

如果这两个接口有一个提供了实现,那么编译器就会报告错误,让程序员解决二义性。

如果两个接口没有提供默认实现,那么编译器不会报错,程序员实现这个方法即可。不实现他们的方法,则类定义为抽象类。

类优先规则

如果类继承的类和继承的接口有相同的方法,那么接口的默认方法都会被忽略。

接口示例

接口与回调

回调:常见的程序设计模式,可以指出某个特定事件发生时应该采取的动作。

下面程序给出了定时器和监听器的操作行为。定时器启动以后,程序弹出一个消息对话框,并等待用户点击 OK 按钮来终止程序的执行。在程序等待用户操作的同时,每隔 10 秒显示一次当前的时间。

public class TimerTest {
    public static void main(String[] args) {
        ActionListener listener = new TimePrinter();

        Timer t = new Timer(10000, listener);
        t.start();
        JOptionPane.showMessageDialog(null, "Quit program?");
        t.stop();
    }

    static class TimePrinter implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("At the tone, the time is" + new Date());
        }
    }
}

Comparator 接口

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.compareTo(second);
        //return first.length() - second.length();
    }
}

具体完成比较时,需要建立一个实例:

LengthComparator comp = new LengthComparator();
if (comp.compare(words[i], words[j]) > 0)) ...

words[i].compareTo(words[j])比较,compare 方法要在比较器对象上调用,而不是在字符串本身上调用。

要对一个数组排序,需要为 Arrays.sort 方法传入一个 LengthComparator 对象:

String[] friends = {"Peter", "Paul", "Mary"};
Arrays.sort(friends, new LengthComparator());

对象克隆

讨论 Cloneable 接口,它指示一个类提供一个安全的 clone 方法。克隆不太常见,细节技术性强,不做深入讨论。

首先,回忆一个包含对象引用的变量建立副本时会发生什么。原变量和副本都是同一个对象的引用:

Employee original = new Employee("John Public", 50000);
Employee copy = original;
copy.raiseSalary(10);//original 也会改变

说明,引用相同的任何一个变量改变都会影响另一个变量。

想要让 copy 的初始状态与 original 相同,但之后他们各自又会有不同的状态,这种情况就要使用 clone 方法:

Employee copy = original.clone();
copy.raiseSalary(10);//original 没有改变

这种拷贝基于 Object 类的 clone 方法属于浅拷贝,如果拷贝的对象的域有子对象,他们之间还是会有联系,改变子对象时,被浅拷贝的对象的子对象也会改变。

通常子对象都是可变的,必须重新定义 clone 方法来建立一个深拷贝,同时克隆所有子对象。

需要确定:

  1. 默认的 clone 方法是否满足要求;
  2. 是否可以在可变的子对象上调用 clone 来修补默认的 clone 方法;
  3. 是否不该使用 clone。

选择 1 或 2,类必须:

  1. 实现 Cloneable 接口;
  2. 重新定义 clone 方法,并指定 public 访问修饰符。

Cloneable接口只是个标签接口,不含任何需要实现的方法:

public class Employee implements Cloneable {
    public Employee clone() throws CloneNotSupportedException {
        Employee cloned = (Employee) super.clone();//对象
        cloned.hireDay = (Date) hireDay.clone();//对象子对象
        return cloned;
    }
}

调用super.clone()得到的是当前调用类的副本,而不是父类的副本。根本没有必用调用this.clone(),并且也用不了 this.clone() 因为这里已经重写了。

lambda 表达式

lambda 的好处

lambda 表达式是一个可传递的代码块,可以在以后执行一次或多次。

有些地方,要将一个代码块传递到某个对象,这个代码块会在将来某个时间调用。在 Java 中传递一个代码段并不容易,不能直接传递代码段。Java 是一种面向对象语言,所以必须构造一个对象,这个对象的类型需要有一个方法能包含所需的代码。

Java SE 8 有了好方法来处理代码块。

lambda 表达式的语法

简单的 lambda 表达式:

(String first, String second)
    -> first.length() - second.length()

也可以像写方法一样,把这些代码放在{}中,并包含显式的return语句:

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

即使没有参数,任然要提供空括号:

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

如果可以推导出一个 lambda 表达式的参数类型,则可以忽略其类型:

Comparator<String> comp = (first, second) ->
    first.length() - second.length();
//编译器可以推导出 first 和 second 必然是字符串,
//因为 lambda 表达式将赋给一个字符串比较器。

如果方法只有一个参数,并且类型可以推出省略,那么可以省略参数括号:

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

无需指定 lambda 表达式的返回类型。它的返回类型会有上下文推到得出。

如果一个 lambda 表达式在一个分支返回一个值,另外一些分支不返回值,这是不合法的:

(int x) -> {
    if(x >= 0) return 1;//不合法
}

函数式接口

对于只有一个抽象方法的接口,需要这种接口的对象时,可以提供一个 lambda 表达式,这种接口成为函数式接口

Arrays.sort(words,
    (first, second) -> first.length() - second.length());

lambda 表达式可以转换为接口:

Timer t = new Timer(1000, event -> {
    System.out.println("At the tone, the time is " + new Date());
    Toolkit.getDefaultToolkit().beep();
})

java 没有增强函数类型,所以不能声明函数类型。

方法引用

可能已经有现成的方法而已完成要传递到其他代码的某个动作:

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

System.out::println是一个方法引用,它们是等价的。

主要有3种情况:

  • object::instanceMethod 如,System.out::println等价x -> System.out.println(x)
  • Class::staticMethod 如,Math::pow等价(x, y) -> Math.pow(x, y)
  • Class::instanceMethod 如,String::compareTolgnoreCase等价(x, y) -> x.compareTolgnoreCase(y)

也可以在方法引用中,使用thissuper参数:

super::instanceMethod
this::instanceMethod

构造引用

Person::newPerson构造器的一个引用,用那个构造器取决于上下文

变量作用域

lambda 表达式可以捕获外围作用域中变量的值,lambda 表达式中只能引用变量的值而不能改变变量的值。另外在 lambda 表达式中引用变量,而这个变量可能在外部改变,这也是不合法的。

规则:lambda 表达式中捕获的变量必须实际上是最终变量。

lambda 表达式的体与嵌套块有相同的作用域。

lambda 表达式中的 this 关键字,指创建这个 lambda 表达式的方法的 this 参数。

处理 lambda 表达式

使用 lambda 表达式重点是延迟执行。

repeat(10, () -> System.out.println("Hello, World!"));

public static void repeat(int n, Runnable action){
    for(int i = 0; i < n; i++) action.run();
}

常用的函数式接口:

略 240 页

内部类

内部类是定义在另一个类中的类,使用原因:

  1. 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。
  2. 内部类可以对同一个包中的其他类隐藏起来。
  3. 想要定义一个回调函数且不想编写大量代码时,使用匿名内部类很便捷。

使用内部类访问对象状态

内部类方法可以访问自身的数据域,也可以访问创建它的外围类对象的数据域,包括私有数据。

public class Outer {
    int num = 10;
    class Inner{
        int count = 20;
        public void print(){
            System.out.println("直接访问外部类属性"+num);
        }
    }
    public void show(){
        System.out.println("外部类。。。");
        System.out.println("在外部类访问内部类属性" + new Inner().count);
        System.out.println("在外部类访问内部类方法:");
        new Inner().print();
    }
    public static void main(String[] args) {
        new Outer().show();
        System.out.println();
        System.out.println("main。。。");
        System.out.println("在mian中访问内部类属性" + new Outer().new Inner().count);
        System.out.println("在mian中访问内部类方法:");
        new Outer().new Inner().print();
    }
}

内部类的特殊语法规则

内部类访问外部类的复杂形式:Outer.this.属性

外部类访问内部类的形式:Inner inner = new Inner()

其他类访问外部类中的内部类的形式:Outer.Inner inner = new Outer().new Inner()

  • 内部类中声明的静态域必须是 final
  • 内部类不能有 static 方法。也允许有,但只能访问外围类的静态域和方法。

私有内部类

class Out {
    private int age = 12;

    private class In {
        public void print() {
            System.out.println(age);
        }
    }
    public void outPrint() {
        new In().print();
    }
}

public class Demo {
    public static void main(String[] args) {
        //此方法无效
        /*
        Out.In in = new Out().new In();
        in.print();
        */
        Out out = new Out();
        out.outPrint();
    }
}

如果一个内部类只希望被外部类中的方法操作,那么可以使用private声明内部类。

上面的代码中,我们必须在Out类里面生成In类的对象进行操作,而无法再使用Out.In in = new Out().new In() 生成内部类的对象。

也就是说,此时的内部类只有外部类可控制;如同是,我的心脏只能由我的身体控制,其他人无法直接访问它。

局部内部类

局部内部类可以对外界完美的隐藏起来。除了 Print 方法没人知道这个内部类。

我们将内部类移到了外部类的方法中,然后在外部类的方法中再生成一个内部类对象去调用内部类方法,这就是局部内部类。

class Out {
    private int age = 12;

    public void Print(final int x) {
        class In {
            public void inPrint() {
                System.out.println(x);
                System.out.println(age);
            }
        }
        new In().inPrint();
    }
}

public class Demo {
    public static void main(String[] args) {
        Out out = new Out();
        out.Print(3);
    }
}

由外部方法访问变量

局部内部类,不仅能够访问包含他们的外部类,还可以访问局部变量,但必须被声明为 final。

匿名内部类

加入只创建这个类的一个对象,就不必命名了,这种了被称为匿名内部类。

类名可以是一个接口,于是内部类就是实现这个接口;也可以是一个类,于是内部类就是对这个类扩展。

abstract class Person {
    public abstract void eat();
}

public class Demo {
    public static void main(String[] args) {
        Person p = new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        };
        p.eat();
    }
}
  • 匿名类没有类名,所以类名没有构造器。
  • 构造器参数会传递给超类构造器。内部类实现接口的时候,不能有任何构造器。
  • 构造参数后面加个{}就代表是匿名内部类。

静态内部类

有时,使用内部类知识为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。可以将内部类声明为 static,以便取消产生的引用。

如果用 static 将内部内静态化,那么内部类就只能访问外部类的静态成员变量,具有局限性。

其次,因为内部类被静态化,因此Out.In可以当做一个整体看,可以直接new出内部类的对象(通过类名访问static,生不生成外部类对象都没关系)。

class Out {
    private static int age = 12;
    //静态内部类
    static class In {
        public void print() {
            System.out.println(age);
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Out.In in = new Out.In();
        in.print();
    }
}
  • 只有内部类可以声明为 static 。
  • 内部类只能访问外围类的静态成员变量,具有局限性。
  • 静态内部类可以有静态域和方法。
  • 声明在接口中的内部类自动成为public static类。

代理

利用代理可以在运行时创建一个实现了一组给定接口的新类。

只有在编译时无法确定需要实现那个接口时才有必要使用。

何时使用代理

有一个便是接口的 Class 对象,要想构造一个实现这些接口的类,需要使用 newInstance 方法或反射找出这个类的构造器。但是,不能实例化一个接口,需要在程序处于运行状态时定义一个新类。

代理类可以在运行时创建全新的类。这样代理类能够实现指定的接口:

  • 指定接口所需要的全部方法。
  • Object 类中的全部方法。

但不能运行时定义这些方法的新代码,而要提供一个调用处理器。

调用处理器是实现了 InvocationHandler 接口的类对象,只有一个方法:

Object invoke(Object proxy, Method method, Object[] args)

创建代理对象

使用 Proxy 类的 newProxyInstance 方法创建一个代理对象,它有三个参数:

  • 一个类加载器。可以使用不同的类加载器,null 表示使用默认的类加载器。
  • 一个Class对象数组,每个元素都是需要实现的接口。
  • 一个调用处理器。现了 InvocationHandler 接口的类对象

使用代理的原因:

  • 路由对远程服务器的方法调用。
  • 在程序运行期间,将用户接口事件与动作关联起来。
  • 为调试、跟踪方法调用。

使用代理和调用处理器跟踪方法调用,并且定义了一个 TraceHander 包装器类存储包装的对象。其中的 invoke 方法打印出被调用方法的名字和参数,随后用包装好的对象作为隐式参数调用这个方法:

public class TraceHandler implements InvocationHandler{
    private Object target;
    public TraceHandler(Object t){
        target = t;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.print(target);
        System.out.print("." + method.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 method.invoke(target, args);
    }

    public static void main(String[] args) {
        Object[] elements = new Object[100];

        for (int i = 0; i < elements.length; i++) {
            Integer value = i + 1;
            InvocationHandler handler = new TraceHandler(value);
            Object proxy = Proxy.newProxyInstance(null, new Class[] {Comparable.class}, handler);
            elements[i] = proxy;
        }
        Integer key = new Random().nextInt(elements.length) + 1;//随机生成一个数 [1, i + 1]
        int result = Arrays.binarySearch(elements, key);//二分搜索法来搜索指定数组,以获得指定对象的位置
        if (result >= 0) System.out.println(elements[result]);

    }
}

只要 proxy 调用了某个方法,这个方法 method 的名字和参数 args 就会打印出来。

代理的特性

  • 代理类是在程序运行过程中创建的,一旦被创建就变成了常规类,与虚拟机中的任何其他类没有什么区别。
  • 代理类都扩展于 Proxy 类。一个代理类只有一个实例域(调用处理器)。任何附加数据都必须存储在调用处理器中。
  • 代理类都覆盖了 Object 类中的方法。覆盖的方法,仅仅调用了调用处理器的 invoke。有些没有被重写定义,如 clone 和 getClass。
  • 没有定义代理类的名字,虚拟机将会自动生成一个类名。
  • 特定的类加载器和预设的一组接口,只能有一个代理类。调用两次 newProxyInstance 方法也只能得到同一个类的两个对象,利用 getProxyClass 方法可以获得这个类:

java Class proxyClass = Proxy.getProxyClass(null, interfaces);

  • 代理类一个是 public 和 final。如果代理类实现的接口都是 public ,代理类就不属于某个特定的包;否则,所有非公有的接口都必须属于同一个包,同时,代理类也属于这个包。通过 Proxy 中的 isProxyClass 方法检测一个特定的 Class 对象是否代表一个代理类。

java System.out.println(Proxy.isProxyClass(elements[3].getClass()));

原文地址:https://www.cnblogs.com/lovezyu/p/9127460.html

时间: 2024-08-02 19:56:47

Java核心技术卷一 4. java接口、lambda、内部类和代理的相关文章

Java核心技术 第六章 接口和内部类

Java核心技术  第六章  接口与内部类 接口: 任何实现Comparable接口的类都需要包含compareTo方法,并且这个方法的参数必须是一个Object对象,返回一个整数数值. 在Java SE 5.0中,Comparable接口已经改进为泛型类型. 接口中所有的方法自动的属于public.因此,在接口中声明方法时,不必提供关键字public. 接口中决不能含有实例域,也不能在接口中实现方法. 要让一个类使用排序服务,必须让它实现compareTo方法,因此必须实现Comparable

Java核心技术卷一 6. java泛型程序设计

泛型程序设计 泛型程序设计:编写的代码可以被很多不同类型的对象所重用. 类型参数:使用<String>,后者可以省略,因为可以从变量的类型推断得出.类型参数让程序更具更好的可读性和安全性. 通配符类型:很抽象,让库的构建者编写出尽可能灵活的方法. 定义简单泛型类 泛型类就是具有一个或多个类型变量的类. //引用类型变量 T ,可以有多个类型变量,public class Pair<T, U>{...} public class Pair<T> { //类定义的类型变量制

Java核心技术卷一 5. java异常、断言和日志

处理错误 由于出现错误而使得某些操作没有完成,程序因该: 返回到一种安全状态,并能够让用户执行一些其他命令 允许用户保存所有操作的结果,并以适当的方式终止程序 需要关注的问题: 用户输入错误 设备错误 物理限制 代码错误 当某个方法不能够采用正常的路径完成它的任务,就可以通过另外一个一个路径退出方法.这种情况下,方法并不返回任何值,而是抛出(throw)一个封装了错误信息的对象.要注意这个方法将会立刻退出,并不返回任何值.调用这个方法的代码也将无法继续执行,异常处理机制开始搜索能够处理这种异常状

Java核心技术卷一 8. java并发

什么是线程 每个进程拥有自己的一整套变量,而线程则共享数据. 没有使用多线程的程序,调用 Thread.sleep 不会创建一个新线程,用于暂停当前线程的活动.程序未结束前无法与程序进行交互. 使用线程给其他任务提供机会 将代码放置在一个独立的线程中,事件调度线程会关注事件,并处理用户的动作. 在一个单独的线程中执行一个任务的简单过程: 将任务代码移到实现了 Runnable 接口的类的 run 方法中. public interface Runnable{ void run(); } Runn

java核心技术卷一

java核心技术卷一 java基础类型 整型 数据类型 字节数 取值范围 int 4 +_2^4*8-1 short 2 +_2^2*8-1 long 8 +_2^8*8-1 byte 1 -128-127       浮点类型 数据类型 字节数 取值范围 小数位数 float 4 10^-38~10^38和-10^-38~-10^38 小数位数6-7 double 4 10^-308~10^308和-10^-308~-10^308 15位小数         boolean 类型和char 类

读《java核心技术卷一》有感

过去一个多月了吧.才囫囵吞枣地把这书过了一遍.话说这书也够长的,一共706页.我从来不是个喜欢记录的人,一直以来看什么书都是看完了就扔一边去,可能有时候有那么一点想记录下来的冲动,但算算时间太紧,很多也是有始无终,毕竟在之前研究研究程序也只是自己的一个爱好而已,但没有想到签了一个程序员的工作.唉,这老天也太捉弄人了吧,让一个学电气工程(强电方向)学生毕业之后去写代码,而且是与硬件完全无关的代码.真是白念几年大学了.行了,就行发这么多牢骚吧. <java核心技术>有两个卷,我只看了卷一,从我的感

Java核心技术之Java8新特性-Lambda表达式

1 总体说明 Java8新特性概述 函数式接口 Lambda表达式(闭包) 2 Java8新特性概述 Oracle公司于2014年3月发布了Java8正式版,该版本是自JDK5.0以来最具革命性的版本. Java8为Java语言.编译器.类库和JVM带来了大量的新特性.接下来的内容将会详细说明Java8在Java语言方面的新特性以及它们的使用场景. 3 函数式接口 Java8引入的一个核心概念是函数式接口(Functional Interfaces):如果一个接口定义一个唯一的抽象方法,那么这个

Java核心技术 卷一 笔记六 Date类

在Java核心技术卷就行了一前期  date类出现的频率很高  所以就对date类进行了小小的整合 Date类有两个 date类 表示特定时间的类 这个构造函数分配一个Date对象并初始化它代表指定的毫秒数,因为被称为"纪元",即1970年1月1日00:00:00 GMT标准基准时间. 就像格林尼治时间一样  作为一种基准值而存在 一般常用的是date转为string类型 String s=new Date(0).toString(); System.out.println(s.toS

java 核心技术卷一笔记 6 .1接口 lambda 表达式 内部类

6.1 接口不是类,是对类的一组需求的描述,这些类需要遵守接口描述的统一格式进行定义.例如:Arrays类中sort方法(可以对对象数组进行排序)前提是对象所属的类必须实现了Comparable 接口. public interface Comparable { int compareTo(Object other) } Comparable public interface Comparable<T> { int compareTo(T other) } Comparable泛型 接口的方法