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方法,克隆一个对象,防止改变原对象的状态
对象的实例域可能也是可变的类对象,默认的克隆不会克隆子对象,所以需要自己定义
- 默认的clone方法是否能满足要求(域都是基本类型或者不可变的类)
- 是否可以在可变的子对象上调用clone方法修补默认的clone
- 是否应该使用clone方法
如果满足需求中的1、2,就必须:
- 实现Cloneable接口
- 重新定义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表达式的任意位置一样
内部类
定义在另一个类中的类
使用内部类的目的:
内部类方法可以访问该类定义所在作用域中的所有数据,包括私有数据
内部类可以对同一包下的其他类隐藏
定义一个回调函数且不想编写大量代码时,可以使用匿名内部类
内部类既可以访问自身数据域也可以访问外部类的数据(自身优先,内部类对象有一个隐式引用指向创建它的外部类对象)
使用外部类引用内部类:outerObject.new InnerClass(constructionParams);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修饰的
内部类中的所有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(),会得到同一个类的两个对象
- JavaSE———接口、lambda表达式、内部类
- 6 接口、Lambda表达式和内部类
- JavaSE实战——面向对象(中) 抽象类,接口,多态,内部类,匿名内部类
- JavaSE实战——面向对象(中) 抽象类,接口,多态,内部类,匿名内部类
- 黑马程序员 JAVASE——内部类
- Java基础学习总结(69)——匿名内部类与Lambda表达式
- 内部类和Lambda表达式
- 黑马程序员——javase基础--多态、内部类、异常
- 10、匿名内部类实现接口,lambda表达式实现函数式接口
- java中的内部类和Lambda表达式
- Lambda表达式和匿名内部类
- Java匿名内部类与Lambda表达式
- lambda表达式代替匿名内部类
- Lambda表达式和匿名内部类
- JAVA8 匿名内部类和lambda表达式
- Java:匿名内部类和Lambda表达式
- 内部类、匿名类、Lambda表达式
- #Java 核心技术卷一阅读笔记# 第六章 接口、lambda表达式与内部类
- GridView使用【GridViewHelper】分组统计
- 堆排序
- Android利用setLayoutParams在代码中调整布局(Margin和居中)
- 使用wordle制作文字云
- 【bat】有线/无线+手动/DHCP切换网络
- JavaSE———接口、lambda表达式、内部类
- SSM框架——使用MyBatis Generator自动创建代码
- 图表Highcharts属性
- 在Ubuntu 16.04下安装 virtualbox 5.0/5.1
- Class.forName()方法详解
- MacBook的浏览器无法连接到服务器,其他应用可以正常使用的解决办法
- 第11周项目4- 利用遍历思想求解图问题(1)
- 共享储物柜的出现提升物流行业投递效率
- lua基础知识二