JavaSE———接口、lambda表达式、内部类

来源:互联网 发布:msdn版本选择windows 编辑:程序博客网 时间:2024/06/16 20:14

接口

主要用来描述类具有哪些功能,而不具体定义这些功能的实现

接口不是类,是对其实现类的一组需求描述

接口中所有方法默认是public abstract修饰的,属性默认是常量(public static final)

public class InterfaceTest implements Comparable<Object> {@Overridepublic int compareTo(Object o) {// TODO Auto-generated method stubreturn 0;}}

所有实现Comparable接口(只定义了compareTo方法)的类都必须在类中定义compareTo方法

接口不可以创建实例对象,所以接口中不能有实例域(类创建的对象,对象的属性),但是可以定义常量

实现接口:将类声明为实现给定的接口、对接口中的所有方法进行定义。关键字——implements

想要比较不同的两个子类对象,可以在公共父类实现Comparable接口,在其中定义final的compareTo方法

接口没有构造方法,不可以创建对象,但可以声明接口的变量(多态的另一种体现:接口变量引用实现接口的类对象)

可以使用instanceof判断某个对象是否实现了某接口:anObject instanceof Comparable

接口可以使用extends关键字继续扩展:public interface Inter2 extends Inter1{}

每个类只能继承一个类,但是可以实现多个接口:class Emp implements Cloneable,Comparable{}

接口和抽象类的区别和联系:

抽象类仍然是类,只能被继承一个,而多个接口可以同时被实现

抽象类不能创建对象,但是有构造方法(为了让子类调用实例化对象),接口没有构造方法(域都是静态常量)

抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象

抽象类要被子类继承,接口要被类实现

接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现

接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量

抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类

抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果

抽象类里可以没有抽象方法

如果一个类里有抽象方法,那么这个类只能是抽象类

抽象方法要被实现,所以不能是静态的,也不能是私有的

interface TestInter{String a = "";Object obj = new Object();public static TestInter get(){return null;}}
java SE 8中,可以在接口中定义静态方法(类方法),java SE 8之前都是讲静态方法放在接口的伴随类(工具类)中,所以jdk1.8之后可以在接口中定义工具方法,不需要单独为接口创建伴随的工具类

默认方法,接口方法的默认实现,default修饰

接口被实现后,添加一个默认方法,实现接口的类重新编译不会报错,如果这个类没有重新编译而直接加载的话,也可以直接调用新增的这个默认方法

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

1.超类优先——如果超类中有这个方法,那么就忽略接口中的方法定义

类优先的规则使得我们在接口中新增一个默认方法,可以确保之前的正常代码不会受到影响

public class InterfaceTest extends TestClass implements TestInter {public static void main(String[] args) {InterfaceTest it = new InterfaceTest();int age = it.getAge();System.out.println(age);//2}}interface TestInter{default int getAge(){return 12;}}class TestClass{public int getAge(){return 2;}}
2.接口冲突——如果同时实现的两个接口中都定义了相同的默认方法,必须覆盖这个方法来解决冲突
public class InterfaceTest implements TestInter ,TestInter2{//会报错,Duplicate default methods named getAge with the parameters () and () are inherited from the types TestInter2 and TestInterpublic static void main(String[] args) {InterfaceTest it = new InterfaceTest();int age = it.getAge();System.out.println(age);}}interface TestInter{default int getAge(){return 12;}}interface TestInter2{default int getAge(){return 2;}}

提示我们必须解决这个二义性问题,只要在InterfaceTest类中提供一个getAge方法就可以了,可以选择两个冲突方法的一个:

public int getAge(){return TestInter.super.getAge();}

所以在接口中使用默认方法重新定义Object类中的方法是没有意义的

回调,callback,指定某个特定事件发生时执行相应动作

比较器,实现了Comparator接口的类的实例

public class InterfaceTest implements Comparator<String>{@Overridepublic int compare(String o1, String o2) {//定义按照字符串长度比较大小的比较方法return o1.length() - o2.length();}public static void main(String[] args) {String[] strs = {"abc","abd1","bca12","aab123"};Comparator<String> comp = new InterfaceTest();for (int i = 0; i < strs.length; i++) {for (int j = i+1; j < strs.length; j++) {if(comp.compare(strs[i], strs[j]) < 0){System.out.println(strs[i]+" < "+strs[j]);}}}                Arrays.sort(strs, comp);//按照自定义比较方式给String数组strs排序      }}

克隆,给实现Cloneable接口的类提供一个安全的clone方法,克隆一个对象,防止改变原对象的状态

对象的实例域可能也是可变的类对象,默认的克隆不会克隆子对象,所以需要自己定义

  1. 默认的clone方法是否能满足要求(域都是基本类型或者不可变的类)
  2. 是否可以在可变的子对象上调用clone方法修补默认的clone
  3. 是否应该使用clone方法

如果满足需求中的1、2,就必须:

  1. 实现Cloneable接口
  2. 重新定义clone方法,指定public修饰

每个数组类型都有一个public的clone方法

关于protected修饰符, 在java中,父类中protected权限的成员变量可以被子类访问,但是还是有条件的,具体如下:

子类中可以直接使用父类的protected变量,父类的protected权限的变量可以被子类继承

子类中可以通过子类对象(子类的子类对象也可以)访问父类的protected成员

“同包或者子类可见”的意思是在不同包的情况下,子类中只有子类对象本身可见

子类中使用父类对象或者其他子类对象访问父类的protected成员都是不可以的

父类中的protected成员被子类继承,在子类中创建父类调用这个成员时,调用的是父类自己的protected成员,但在包外不可见,所以无法调用;而子类从父类那里继承了这个protected成员,调用的是自己的protected成员,所以可以使用

lambda表达式

一个可传递的代码块,可以在以后执行一次到多次

表达式形式:参数、箭头(->)以及一个表达式(表达式可以是一个{}代码块)

没有参数时,()不可以省略:()->{System.out.println()}

如果可以推出lambda表达式参数类型,可以省略类型

如果只有一个参数,且类型可以推出,还可以省略括号

        public static void main(String[] args) {String[] planets = {"abcasd","aab","abascd","aa"};Comparator<String> comp = (String first , String second) -> first.length() - second.length();Arrays.sort(planets, comp);System.out.println(Arrays.toString(planets));ActionListener listener = event -> System.out.println("The time is"+new Date());Timer t = new Timer(1000, listener);t.start();JOptionPane.showMessageDialog(null, "Quit program?");System.exit(0);}

函数式接口:

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

lambda表达式可以看作是一个函数,而不是对象,只不过这个函数可以转换成接口

方法引用:用::操作符分隔方法名和对象或者类名

object::instanceMethod

Class::staticMethod

Class::instanceMethod

前两种情况等价于提供方法参数的lambda表达式:System.out::println《===》x -> System.out.println(x)

第三种情况,第一个参数会成为方法目标:String::compareToIgnoreCase《===》(x,y) -> x.compareToIgnoreCase(y)

方法引用不能独立存在,总是会转换成函数式接口的实例

可以在方法引用中使用this参数,this::equals《===》x -> this.equals(x)

也可以使用super参数,super::instanceMethod《===》this.super.instanceMethod(params)

lambda表达式中,只能引用值不会改变的变量(无论是lambda表达式中还是外部),即lambda表达式捕获的变量必须实际上是最终变量(初始化后不再赋值)

int a = 1;a++;Timer t = new Timer(1000, event -> {        System.out.println(a);//Error:Local variable a defined in an enclosing scope must be final or effectively final});
int a = 1;Timer t = new Timer(1000, event -> {        a++;//Error:Local variable a defined in an enclosing scope must be final or effectively finalSystem.out.println(a);});

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

public class TestLambda {
public static void main(String[] args) {Timer t = new Timer(1000, event -> {System.out.println(this.toString());});t.start();JOptionPane.showMessageDialog(null, "exit OK?");System.exit(0);}}


这个this.toString()方法和在main方法中调用this.toString()方法是一样的,this并不是指Timer而是TestLambda对象

lambda表达式中使用this和出现在使用lambda表达式的任意位置一样

内部类

定义在另一个类中的类

使用内部类的目的:

内部类方法可以访问该类定义所在作用域中的所有数据,包括私有数据

内部类可以对同一包下的其他类隐藏

定义一个回调函数且不想编写大量代码时,可以使用匿名内部类

内部类既可以访问自身数据域也可以访问外部类的数据(自身优先,内部类对象有一个隐式引用指向创建它的外部类对象)

public class TestInner {private String str = "test";public class InnerClass{public void test(){System.out.println("test,print str in inner:"+str);}}public static void main(String[] args) {InnerClass inner = new TestInner().new InnerClass();inner.test();//test,print str in inner:test}}
内部类声明为private的,这样就只有外部类可以构造内部类对象
只有内部类可以是private修饰的
使用外部类引用内部类:outerObject.new InnerClass(constructionParams);
内部类中的所有static域都必须是final的,一个静态域只有一个实例,但是每个外部类对象都会分别有一个单独的内部类实例,如果该静态域不是final的,可能就不是唯一的
内部类中不可以有static方法
public class TestInner {private String str = "test";public class InnerClass{public static  int a =1;//Error:The field a cannot be declared static in a non-static inner type, unless initialized with a constant expressionpublic static void test(){//Error:The method test cannot be declared static; static methods can only be declared in a static or top level typeSystem.out.println("test,print str in inner:");}}}
内部类的特殊:
内部类可以访问外部类私有数据,但是常规类不可以访问另一常规类的私有数据
编译器会将内部类翻译成用$分隔外部类名和内部类名的常规类文件:OuterClass$InnerClass.class
编译器翻译外部类时会将外部类当做参数传给一个新增的static方法access$0,然后内部类翻译时会调用这个方法

局部内部类:在块中定义一个内部类

不能使用public或者private进行声明,作用域被限定在声明这个局部类的块中

可以对外部完全隐藏,即使是相同类的其他方法都不可以访问,只有声明这个内部类的方法中才能访问

public class TestInner {private String str = "test";public void test(){int a =1;System.out.println(a);class InnerClass{public void printStr(){System.out.println("inner:"+str+(a++));                                //Local variable a defined in an enclosing scope must be final or effectively final                                //局部类中只可以访问实际上是final的局部变量                                //java8之前必须将要引用的变量声明为final,java8不需要                        }}InnerClass ic = new InnerClass();ic.printStr();}public static void main(String[] args) {new TestInner().test();        //1                //inner:test                //局部内部类既可以访问外部变量又可以访问包含它的块的局部变量        }}

匿名内部类:只创建这个类的一个对象,不需要命名,直接使用

格式:new SuperClass(constructionParams){//或者接口

inner class and data

}

public class TestInner {private String str = "test";public void test(){int a =1;System.out.println(a);InnerClass ic = new InnerClass(){//创建一个实现InnerClass接口的类的对象public void printStr(){System.out.println("inner:"+str);}};ic.printStr();}public static void main(String[] args) {new TestInner().test();}}interface InnerClass{void printStr();}
构造器必须和类名一致,匿名内部类没有类名,所以没有构造器,都是将构造器参数传给父类构造器(实现接口时不需要任何参数)
静态内部类
只有内部类可以被声明为static,静态内部类的对象不生成外部类对象的引用(不使用外部类的数据)
静态内部类可以有静态方法和静态域
声明在接口中的内部类默认public static修饰
public class TestStaticInner {private String str = "123";private int a = 1;static class InnerClass{private static String str = "str";static void test(){new TestStaticInner().test();System.out.println("inner"+str);}}public void test(){System.out.println("outer"+str);}public static void main(String[] args) {new InnerClass().test();                //outer123                //innertest       }}

代理

使用代理可以实现在运行时创建一个实现了一组接口的新类(编译时无法确定需要实现)

使用场景:

一个表示接口的Class对象(不一定是一个接口),它的确切类型在编译时无法确定。代理类可以在运行时创建一个全新的类,这个类能够实现指定接口(指定接口所需要的全部方法、Object类的全部方法)

不能在运行时定义这些方法的新代码,而是提供一个调用处理器(invocationhandler),调用处理器是实现了InvocationHandler接口(这个接口中只有一个invoke方法)的类对象

创建代理对象

类加载器:使用null表示默认类加载器

Class对象数组:每个元素都是需要实现的接口

调用处理器:实现了InvocationHandler接口(这个接口中只有一个invoke方法)的类对象

class TraceHandler implements InvocationHandler {private Object target;@Overridepublic Object invoke(Object proxy, Method method, Object[] args)//args对应proxy实例时的new Class[]{Comparable.class}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 TraceHandler(Object target) {super();this.target = target;}}public class ProxyTest{public static void main(String[] args) {Object[] elems = new Object[100];for (int i = 0; i < elems.length; i++) {Integer value = i + 1;TraceHandler th = new TraceHandler(value);Object proxy = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, th);elems[i] = proxy;}Integer key = new Random().nextInt(elems.length) + 1;int result = Arrays.binarySearch(elems, key);if(result >= 0 ){System.out.println(elems[result]);}}}

代理类的特点:

程序运行时创建,一旦创建就变成常规类

所以代理类都扩展了Proxy类,一个代理类只有一个实例域——调用处理器

所有的代理类都覆盖了Object类的tostring、equals、hashCode,Object中的其他方法(clone、getClass)没被覆盖

代理类默认public final修饰

特定的类加载去和相同的一组接口,只能用一个代理类,调用两次newProxyInstance(),会得到同一个类的两个对象


阅读全文
0 0