目录:
基本数据类型 8种 及对应的 类型封装器:
byte, short, int, long -> Long,Integer,Short,Byte :Number
float, double -> Double,Float :Number
char, boolean -> Character,Boolean
1. java的其他方面是完全面向对象的,但基本数据类型不是,和c,c++一样,基本数据类型仅表示单个值,因为将基本数据类型设计为对象会极大地降低性能。
类型封装器:基本类型提供了性能方面的好处,但有时会需要对象的表现形式:传递引用及许多数据结构都是针对对象进行操作的。
数值类型封装器都继承自抽象类Number
2. 整型变化:
3. 字符型, char。
char ch1,ch2; ch1 = 88; ch2 = 'x'; System.out.println(ch1); // X System.out.println(ch2); // x System.out.println(++ch1); // Y System.out.println(++ch2); // y
Integer(int num) Integer(String str) // NumberFormatException Integer iob = new Integer(100); int i = iob.intValue(); // 从jdk 5开始,增加了两个重要特性,自动装箱和自动拆箱。 // 极大地简化了一些算法代码,也有助于防止出错,对泛型,集合框架非常重要。 Integer iob = 100; int i = iob;
字面量0.56的类型是double,即浮点数的默认类型是double,而float 需要写作0.56f
同理1的类型是int
数组
java中只有动态数组,同时会严格检查数组下标,避免越界。
java中的数组是作为类的对象实现的。
// 申请内存与c++相同,但无需delete操作 int x[] = new int[12]; // 也可以直接初始化,java会自动创建足够大的数组,以容纳数组初始化器里的元素 int days[] = {31,28,31,30,31,30,31}; // 多维数组 int twoD[][] = new int[4][5]; // 手动分配第二维 int twoD2[][] = new int[4][]; twoD2[0] = new int[1]; twoD2[1] = new int[2]; twoD2[2] = new int[3]; twoD2[3] = new int[4]; // 初始化器 int twoD3[][] = { {1,2}, {1,2,3}, {1,2,3,4} }; // 另一种数组声明语法 int[] y = new int[12]; //int x[] = new int[12]; int[][] twoD4 = new int[4][5];//twoD[][] = new int[4][5];
在java中数组也是由类实现的,数组创建后如果没有初始化数据,则都会是默认值,即布尔类型默认为false,数值默认为0,其他默认为null。
指针
java 中不支持程序员操作指针,因为指针可能突破java执行环境和宿主计算机之间的防火墙,操作java运行时系统之外的地址。这与java的设计理念不合。
同时因为引用对象的变量的传递,实质是对指针的拷贝,所以java中没有真正的引用传递操作,因为JAVA认为没有指针也足够满足用户的需求。而c#中则可以使用ref。
public class test { // change不是在改变传入对象的状态,而是改了str的指向 public static void change(String str){ s = "aaa"; } public static void main(String[] args) { // TODO Auto-generated method stub String s = "1223"; change(s); System.out.println(s); } } // 结果: 1223, 证明其是值传递
运算符
与c相比多出来的
>>>: 按位右移 补0 (对某些非数值的数据进行移位操作时,并不希望出现符号扩展)
instanceof 判别对象是否是某类的实例
布尔逻辑运算符:&,|,^,&=,|=, ^= 不短路的布尔逻辑运算
运算符分类
算数运算符:
+,-,*, / , %, ++, --, +=,-=,*=,/=,%=
位运算符:
~,&,|,^,>>, >>>, <<, &=, |= , ^= , >>= , >>>=, <<=
关系运算符:
==,!=,>,<,>=,<=
布尔逻辑运算符:
&,|,^, ||, &&,!,&=,|=,^=,==,!=,?:
赋值运算符:
=
float a=3; double b=2; b+=a; b=b+a; a+=b; a=a+b; //报错, a+b时会自动把a提升为double,而double类型不能直接赋值给float a=(float)(a+b)
// int类型的值 和 float类型的值相加,得到的结果是float而不是double int a =1; float b = 6; b = b +a;
java里没有运算符重载,类的比较要用equals, 而不能用==,后者比较的是两个地址,即是恒等于
整型是会溢出的,所以有 i + 1< i 可能成立
同样 x>y||x<=y 也可能不成立
Double x = Double.NaN; Float y = Float.NaN; if(!(x>y||x<=y)){ System.out.println("ok"); }
String:
1. 每个字符串都是String的对象。
2. 字符串对象是不可变的,修改字符串实际是创建了一个新的对象。
3. 大量的字符串拼接操作,使用 StringBuffer和StringBuilder,它们是String的对等类,但它们允许修改字符串。
4. 常用属性,方法: length, equals(), charAt()
5. 命令行参数 main(String args[]){}
class CommandLine{ public static void main(String args[]){ for(int i=0; i< args.length;i++){ System.out.println("args["+i+"] : " + args[i]); } } } // java CommandLine this is a test 100 - 1
类 :
1. java中 未显式定义构造函数的类,编译器会自动添加默认构造器,将所有存储属性初始化为默认值。
类的方法名也可以与类同名,因为方法有返回值,而构造函数的返回值是缺省的。
创建类的实例,可以不用构造方法:
比如调用对象的clone()方法,从内存上对已有对象的影印。
运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法,是从文件中还原类的对象,也不会调用构造函数。
2. java中没有析构函数,但提供了类似的finalize()方法。在回收类的对象时执行本方法。
3. 访问控制public ,private, protected
4. static, 静态变量的初始化如果需要计算,仅在第一次加载类时执行一次。
在类被加载时static修饰的成员字段被初始化,与类关联,只要类存在,static字段就存在。一个static字段单独划分一块存储空间,不与具体的对象绑定在一起,该存储空间被类的各个对象所共享。
区别: java中 静态变量和方法可以被实例对象调用(不推荐这样),而不像其他语言那样必须要用类名调用。// 即可以 this.*,而非必需Class.*
5. final ,用于变量相当于const,用于方法则方法不可重写,用于类则类不可继承。
6. 嵌套类, 内部类(非静态的嵌套类)。
7. 可变参数 ,使用数组传递未知数量参数,需要手动将这些参数打包为数组,不仅繁琐,而且容易出错。所以有了可变参数的诞生,将封装数组的操作交给了编译器。
void vaTest(int ... v){} // 与swift相比,省略号放在了前面。
可变参数的方法存在模糊性(Ambiguous)
class test { void vaTest(int ... v){} void vaTest(boolean ... v){} void vaTest(int n, int ... v){} public static void main(String a[]){ vaTest(1,2,3); vaTest(true); vaTest(); // Error,Ambiguous vaTest(1);// Error,Ambiguous } }
继承:
8. super 可以访问父类中被子类重写的属性和方法 super , this
super.属性; //访问最近超类的属性
super.方法 // 访问最近超类的方法
super(); //访问最近超类的构造函数
构造函数 调用机制:在类层次中,从超类到子类按照继承的顺序调用构造函数。
类A继承类B, 在实例化类A时,会先实例化类B,如果构造函数里有super(),则super()必须是子类构造函数中的第一条语句,而如果没有super(),那么也会先执行超类的默认构造函数或无参构造函数。
class A{ A(){ System.out.println("init A"); } } class B extends A{ B(){ System.out.println("init B"); } B(int x){ System.out.println("init2 B"); } } class C extends B{ C(){ System.out.println("init C"); } C(int x){ System.out.println("init2 C"); } C(boolean x){ super(2); System.out.println("init3 C"); } } class show2{ public static void main(String args[]){ C one = new C(); /** * init A * init B * init C */ C two = new C(1); /** * init A * init B * init2 C */ C three = new C(true); /** * init A * init2 B * init2 C */ } }
9. 方法重写
java中没有虚函数,可以直接重写超类的方法。
class A{ void callme(){ System.out.println("A's callme method"); } } class B extends A{ void callme(){ System.out.println("B's callme method"); } } class C extends B{ void callme(){ super.callme(); System.out.println("C's callme method"); } } class show2{ public static void main(String args[]){ A a = new A(); B b = new B(); C c = new C(); A r; r = a; r.callme(); System.out.println("\n"); r = b; r.callme(); System.out.println("\n"); r = c; r.callme(); } }
10. 抽象类 ,abstract
抽象类内部有一个抽象方法,需要显式地添加abstract修饰词,不能直接创建实例
abstract class ab{ abstract void callme(); }
11. final 阻止方法重写。
1. 将方法声明为final ,有时可以提高性能。编译器可以自由地内联对这类方法的调用(当调用小的final方法时,编译器通常可以复制子例程的字节码,直接和调用方法的编译代码内联到一起。从而可以消除方法调用所需的开销。内联是final方法才有的选项。)。
2 . 通常java在运行时动态分析对方法的调用,这称为后期绑定。而final 方法的调用却可以在编译时解析,这称为早期绑定。
final 阻止继承。 抽象类本身是不完整的,需要子类提供完整的实现,所以不能同时将类声明为abstract 和 final
Object类: java中所有其他类的超类,它的引用变量可以指向任何其他类的对象,包括数组。所有类的对象都可以使用Object类的方法。
Object clone(); 创建一个和将要复制的对象完全相同的新对象。
boolean equals(Object objects); 判断一个对象是否和另一个对象相等。
void finalize(); 在回首不再使用的对象之前使用
final Class<?>getClass() 在运行时获取对象所属的类
int hashCode() 返回与运行对象相关联的散列值
final void notify() 恢复执行在调用对象上等待的某个线程
final void notifyAll() 恢复执行在调用对象上等待的所有线程
String toString() 返回一个描述对象的字符串
final void wait() 等待另一个线程的执行
final void wait(long milliseconds)
final void wait(long milliseconds,int nanoseconds)
包 package
包是多个类的容器,作用类似于命名空间。
包是一种命名机制,也是一种可见性的控制机制,可以在包内定义包外部不能访问的类(不加访问修饰符)。
访问控制
package p1; public class Protection { int n = 1; private int n_pri = 2; protected int n_pro = 3; public int n_pub = 4; public Protection(){ System.out.println("base constructor"); System.out.println("n = " + n); System.out.println("n_pri = " + n_pri); System.out.println("n_pro = " + n_pro); System.out.println("n_pub = " + n_pub); } } package p1; public class Derived extends Protection { Derived(){ System.out.println("Derived constructor"); System.out.println("n = " + n); // class only //System.out.println("n_pri = " + n_pri); System.out.println("n_pro = " + n_pro); System.out.println("n_pub = " + n_pub); } } package p1; public class SamePackage { SamePackage(){ Protection p = new Protection(); System.out.println("SamePackage constructor"); System.out.println("n = " + p.n); // class only //System.out.println("n_pri = " + p.n_pri); System.out.println("n_pro = " + p.n_pro); System.out.println("n_pub = " + p.n_pub); } } package p2; import p1.Protection; public class Protection2 extends Protection { Protection2(){ System.out.println("Derived other package constructor"); // class or package only //System.out.println("n = " + n); // class only //System.out.println("n_pri = " + n_pri); System.out.println("n_pro = " + n_pro); System.out.println("n_pub = " + n_pub); } } package p2; public class otherPackage { otherPackage(){ p1.Protection p = new p1.Protection(); System.out.println("otherPackage constructor"); // class or package only //System.out.println("n = " + p.n); // class only //System.out.println("n_pri = " + p.n_pri); // class,subclass or package only //System.out.println("n_pro = " + p.n_pro); System.out.println("n_pub = " + p.n_pub); } }
导入包:
import java.util.date;
import java.io.*;
接口 interface
可声明为public 或 默认访问级别,也可以用abstract
java将接口从其实现中完全抽象出来,接口与抽象类很像,但接口还有其他功能:一个类可以实现多个接口,但一个类只能继承一个超类。
如果在类实现的两个接口中都声明了同一个方法,则这两个接口的客户都将可以使用这个方法。
实现接口的方法必须被声明为 public。
接口是对事物行为、属性的更高级别的抽象,放入接口中的全部都是不可变更的东西。同时接口是公开的,里面不能有私有的方法或变量。
接口中没有实例变量,接口内的方法没有方法体。
接口中可以声明变量,它们会被隐式地标识为 final 和 static。 实现接口的类不能修改它们。
接口中所有的变量和方法都被隐式地声明为 public。
interface CallBack{ int x = 0; void callback(int param); }
如果类包含了一个接口,但并没有实现该接口定义的全部方法,那么必须将类声明为抽象类
abstract class Incomplete implements CallBack { int a,b; void show { System.out.println(a + " " + b); } // ... }
接口扩展接口
interface A{ void method1(); } interface B extends A{ void method2(); }
接口中的变量
// 类似C 中的 #define 常量或 const 声明 // 如果接口不包含方法,那么实现该接口就相当于把接口中的常量导入到类名称空间中 import java.util.Random; interface ShareConstants{ int NO = 0; int YES = 1; int MAYBE = 2; int LATER = 3; int SOON = 4; int NEVER = 5; } class Question implements ShareConstants{ Random rand = new Random(); int ask(){ int prob = (int) (100 * rand.nextDouble()); if (prob < 30) { // 在实例方法中直接使用静态变量,即可以 this.NO,而非必需Question.NO return NO; // 30% } else if (prob < 60) { return YES; // 30% } else if (prob < 75) { return LATER; // 15% } else if (prob < 98) { return SOON; // 13% } else { return NEVER; // 2% } } } class AskMe implements ShareConstants{ static void answer(int result){ switch(result){ case NO: System.out.println("No"); break; case YES: System.out.println("Yes"); break; case MAYBE: System.out.println("Maybe"); break; case LATER: System.out.println("Later"); break; case SOON: System.out.println("Soon"); break; case NEVER: System.out.println("Never"); break; } } public static void main(String a[]){ Question q = new Question (); answer(q.ask()); answer(q.ask()); answer(q.ask()); answer(q.ask()); answer(q.ask()); } }
嵌套接口 :将接口声明为某个类或另一个接口的成员。private,public, protected,或默认
// a nested interface example class A{ //a nested interface public interface NestedIF{ boolean isNotNegative(int x); } } class B implements A.NestedIF{ public boolean isNotNegative(int x){ return x < 0 ? false : true; } } class NestedDemo{ public static void main(String args[]){ A.NestedIF nif = new B(); if (nif.isNotNegative(10)) System.out.println("ssss"); } }
jdk 8 为接口增加了一个新的特性,在jdk 8之前,接口只能定义“有什么”,而不能定义“如何实现”。
从jdk 8开始,可以在接口方法中添加默认实现。然而,默认实现只是构成了一种特殊用途,接口最初的目的没有改变。
接口添加默认实现的好处有:
1. 提供了可选功能,接口方法 带有了默认实现,则实现该接口的类就不必在不需要该功能时提供占位符实现了。
2. 可以优雅地随时间演变接口,当为一个广泛使用的接口添加一个新方法时,不必破环现有代码。
// 接口默认方法的定义类似于为类定义方法,区别在于,需带关键字default public interface MyIF{ int getNumber(); default String getString(){ return "Default String"; } }
因为接口依然不能有实例变量。所以接口的特征没有改变,依然不能维护状态信息(这是接口和类的决定性区别)。
所以默认方法的引入,只是带来了额外的灵活性。并未有C++那样的多重继承。
但类确实可以从接口中继承一些行为,在一定程度上支持多重继承,也就可能发生名称冲突。
解决办法:
1. 在所有情况下,类实现的优先级高于接口的默认实现。类中的重写将覆盖接口中的默认方法,即便几个接口发生名称冲突,也会一起被重写。
2. 当类实现的两个接口提供了相同的默认方法,但是类没有重写该方法时,则会发生错误。
3. 如果一个接口继承了另一个接口,并且两个接口定义了相同的默认方法。那么继承接口的版本具有更高优先级。
jdk 8 新增的另一个特性是,接口可以定义一个或多个静态方法。且接口定义的静态方法可以独立于任何对象调用。
实现接口的类或子接口不会继承接口中的静态方法。
public interface MyIF{ int getNumber(); default String getString(){ return "Default String "; } static int getDefaultNumber(){ return 0; } } // 调用 int defNum = MyIF.getDefaultNumber();
动态方法调度(运行时多态的基础),使用超类或接口,依赖倒置,有利于解偶,但要在运行时动态查找方法,性能上会增加额为负担。
所以在性能要求苛刻的代码中,应当谨慎小心。
异常处理
在不支持异常处理的语言中,必须手动检查和处理错误,而java的异常处理避免了这些问题,并且在处理过程中采用面向对象的方式管理运行时错误。
java异常是用来描述在一段代码中发生的异常情况的对象。当出现引起异常的情况时,就会创建用于表示异常的对象,并在引起错误的方法中抛出异常对象,方法可以选择自己处理异常,也可以继续传递异常,无论采用哪种方式,在某一点都会捕获并处理异常,异常可以由java运行时系统生成,也可以通过代码手动生成,前者与那些违反java语言规则或java执行环境的基础性错误有关,后者通常用于向方法的调用者报告某些错误条件。
5个关键字: try ,catch , throw ,finally ,和 throws (java的检查型异常处理,c#中舍弃了)
jdk 7开始 try语句增加了一种支持自动资源管理的新形式,称为带资源的try。
所有异常都是内置类 Throwable的子类。
紧跟Throwable之下的是两个子类,将异常分为两个不同的分支。
Exception:既可以用于用户程序应当捕获的异常情况,也可以用于创建自定义的异常类型的子类。
RuntimeException
Error:定义了在常规环境下不希望由程序捕获的异常,由java运行时系统使用,以指示运行时环境本身出现了某种错误。如堆栈溢出。通常是为了响应灾难性的失败而创建。应用程序通常不处理这类异常。
Throwable 重写了 toString()方法,从而可以返回一个包含异常描述的字符串。
未捕获的异常:
如果程序没有捕获异常,当Java运行时系统检测到错误操作时,它会构造一个新的异常对象,然后抛出,这会导致程序终止执行。
因为异常一旦抛出,就必须要有一个异常处理程序捕获该异常,并立即进行处理。如果程序员没有提供任何自己的异常处理程序,该异常就会由Java运行时系统提供的默认处理程序捕获,默认处理程序会显示一个描述异常的字符串,输出异常发生点的堆栈踪迹并终止程序。堆栈轨迹会显示导致错误的方法的调用序列,可以精确定位导致错误发生的步骤序列,帮助程序员调试。
程序员自己处理异常 try ... catch:
1. 可以修复错误,2.阻止程序自动终止。
大部分设计良好的catch子句,都应当能够分辨出异常情况,然后继续执行。
try-catch 语句由一个 try 块后跟一个或多个 catch 子句构成,这些子句指定不同的异常处理程序。引发异常时,运行时系统会查找处理此异常的 catch 语句。如果当前执行的方法不包含这样的 catch 块,则查看调用当前方法的方法,然后会遍历调用堆栈。如果找不到 catch 块,则 CLR 会向用户显示一条有关未处理异常的消息并停止执行程序。
使用多条catch语句时,异常子类必须位于所有超类之前。否则会编译错误 “Unreachable catch block”
public class test { public static void main(String args[]){ try{ try{ // int a = args.length; // System.out.println("a = " + a); // int b = 42 / a; // System.out.println(b); // int c[] ={1}; c[42] = 99; throw new NullPointerException("demo"); } catch(ArithmeticException e){ System.out.println("Divide by 0:" + e); } catch(ArrayIndexOutOfBoundsException e){ System.out.println("Array index oob:"+ e); } finally{ System.out.println("finally"); } System.out.println("after"); } catch(Exception e){ System.out.println("Exception:"+ e); } } }
throws:
如果方法可能引发自身不进行处理的异常,就必须指明这种行为,以便方法的调用者能够防备这些异常。 throws子句列出了方法可能抛出的异常类型。
除了Error和 RuntimeExceptin 及其子类类型,对于所有其他类型的异常都必需这样处理。
package throwsDemo; public class ThrowsDemo { public static void main(String args[]){ try { throwOne(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } static void throwOne() throws IllegalAccessException{ System.out.println("Inside throwOne"); throw new IllegalAccessException("demo"); } }
在java.lang中,Java定义了一些异常类,这些异常类中最常用的是RuntimeException的子类。这些异常不需要包含在方法的throws列表中
异常 | 含义 |
ArithmeticException | 算术错误,例如除0 |
ArrayIndexOutOfBoundsException | 数组索引越界 |
ArrayStoreExcepion | 使用不兼容的类型为数组元素赋值 |
ClassCastException | 无效转换 |
EnumConstantNotPresentException | 使用未定义的枚举值 |
IllegalArgumentException | 使用非法参数调用方法 |
IllegalMonitorStateException | 非法的监视操作,例如等待未锁定的线程 |
IllegalThreadStateException | 环境或应用程序处于不正确的状态 |
IndexOutOfBoundsException | 某些类型的索引越界 |
NegativeArraySizeException | 使用负数长度创建数组 |
NullPointerException | 非法使用空引用 |
NumberFormatException | 字符串到数值格式的无效转换 |
SecurityException | 违反安全性 |
StringIndexOutOfBounds | 字符串索引越界 |
TypeNotPresentException | 类型未定义 |
UnsupportedOperationException | 不支持的操作 |
ClassNotFoundException | 类未定义 |
CloneNotSupportedException | 试图复制没有实现Cloneable接口的对象 |
IllegalAccessException | 对类的访问被拒绝 |
InstantiationException | 试图实例化一个抽象类或接口 |
InterruptedException | 一个线程被另一个线程中断 |
NotSuchFieldException | 请求的域变量不存在 |
NotSuchMethodException | 请求的方法不存在 |
ReflectiveOperationExcepton | 与反射相关的异常的超类 |
public class ThrowsDemo { public static void main(String args[]){ try{ throwOne(); } catch(Exception e){ e.printStackTrace(); } } static void throwOne(){ System.out.println("Inside throwOne"); throw new ClassCastException("demo"); } }
自定义异常
class MyException extends Exception { private int detail; MyException(int a){ detail = a; } public String toString(){ return "MyException["+detail+"]"; } } class ExceptionDemo{ static void compute(int a) throws MyException{ System.out.println("Called compute("+a+")"); if (a>10){ throw new MyException(a); } System.out.println("Normal exit"); } public static void main(String args[]){ try { compute(1); compute(20); } catch (MyException e) { // TODO Auto-generated catch block System.out.println("Caught: "+e); } } }
链式异常:从jdk1.4 开始,链式异常呗包含进入异常子系统。通过链式异常,可以为异常关联另一个异常。
为了使用链式异常,Throwable 增加了两个构造函数和两个方法。
为异常设置原因:
Throwable(Throwable causeExc)
Throwable(String msg, Throwable causeExc)
Throwable initCause(Throwable causeExc)
查询引发异常的原因:
Throwable getCause() // 返回引发当前异常的异常,如果没有则返回null
public class ChainExcDemo { static void demoproc(){ NullPointerException e = new NullPointerException("top layer"); e.initCause(new ArithmeticException("cause")); throw e; } public static void main(String args[]){ try{ demoproc(); }catch(NullPointerException e){ System.out.println("Caught: " + e); System.out.println("Original cause: " + e.getCause()); } } }
jdk 7 新增3个特性:
1. 带资源的try,当资源不再需要时会自动释放。
2. 多重捕获,允许通过相同的catch子句捕获多个异常,每个多重捕获参数都被隐式声明为final
public class MultiCatch { public static void main(String args[]){ int a = 10,b = 0; int vals[] = {1,2,3}; try{ int result = a / b; vals[10] =19; } catch(ArithmeticException|ArrayIndexOutOfBoundsException e){ System.out.println("Exception Caught:" + e); } System.out.println("after"); } }
3. 更精确地重新抛出 more precise rethrow(最后重新抛出 final rethrow)
更精确地重新抛出 会对重新抛出的异常类型进行限制,只能重新抛出满足以下条件的经检查的异常:由关联的try代码块抛出,没有被前面的catch子句处理过,并且是参数的子类型或者超类型。 为了强制使用这一特性,catch参数须被有效地或者显式地声明为final
class FirstException extends Exception{ FirstException(String s){ super(s); } } class SecondException extends Exception{ SecondException(String s){ super(s); } } public class Rethrowing { // f会抛出一个异常 public static void f(boolean a) throws FirstException, SecondException { System.out.println("originating the exception in f()"); if(a){ throw new FirstException("thrown from f()"); } else{ throw new SecondException("thrown from f()"); } } // g中捕获f抛出的Exception异常并重新抛出 // 报错,更精确的异常抛出 catch子句中抛出的异常必须来自try代码块,否则编译器无法解析 // 这里 throws子句需改为 throws Exception,恢复为普通的重新抛出异常 // public static void g() throws FirstException, SecondException { // try { // f(true); // } // catch(Exception e) { // System.out.println("Inside g(), e.printStackTrace()"); // // // 如果e 被重新赋值为其他异常 // e= new FirstException("22"); // throw e; // } // } // 更精确的重新抛出 // 即使 catch 子句的异常参数e的类型是 Exception, // 但 throws 子句中却可指定异常类型为 FirstException 和 SecondException。 // Java SE 7编译器要求语句抛出的异常必须来自于 try 块, // 并且 try 块抛出的异常只能是 FirstException 和 SecondException。 public static void g()throws FirstException, SecondException{ try { f(true); } catch(final Exception e) { System.out.println("Inside g(), e.printStackTrace()"); throw e; } } public static void main(String[] args){ try { g(); } catch(Exception e) { System.out.println("Caught in main, e.printStackTrace()"); e.printStackTrace(); } } }
多线程:
java内置支持多线程。多线程是特殊形式的多任务处理
多线程程序包含同时运行的多个部分,每个部分被称为一个线程,并且每个线程定义了一个单独的执行路径。
多任务处理 有两种类型: 基于进程和基于线程。
进程本身是程序,基于进程的多任务处理就是允许计算机同时运行多个程序的特性。程序时调度程序的最小代码单元。
而基于线程的多任务环境中,最小的可调度代码单元是线程。这意味着单个程序可以同时执行多个任务。
Thread类 和 Runnable接口
Thread作为线程代理,定义了一些管理线程的方法:
getName(),getPriority(), isAlive(), join() /*等待线程终止*/, run()/*线程的入口点*/, sleep()/*线程挂起*/, start()/*调用线程的run()方法启动线程*/
1. 主线程: Thread.currentThread() (main/*默认名称*/,5/*优先级*/,main/*线程组*/)
在java程序启动时,会立即开始运行的一个线程。
其他子线程都是从主线程产生的。
通常主线程必须是最后结束执行的线程,因为它要执行各种关闭操作。
public class CurrentThreadDemo { public static void main(String args[]){ Thread t = Thread.currentThread(); System.out.println("Current thread: "+ t); t.setName("my thread"); System.out.println("after name change: "+ t); try{ for(int n= 5; n>0;n--){ System.out.println(n); Thread.sleep(1000); } } catch(InterruptedException e){ System.out.println("InterruptedException:"+ e); } } }
创建线程:
java提供了两种方法: 实现Runnable接口,扩展Thread类
实现Runnable接口:
只需实现run方法,就像main线程一样,区别在于run方法为程序中另外一个并发线程的执行建立了一个入口点,当run方法返回时,这个线程将结束。
class NewThread implements Runnable { private Thread t; private String name; NewThread(String threadName){ name = threadName; t = new Thread(this,threadName); System.out.println("Child thread:"+ t); t.start(); } @Override public void run() { // TODO Auto-generated method stub try{ for(int i =5; i>0;i--){ System.out.println("Child thread "+name+":"+ i); Thread.sleep(500); } } catch(InterruptedException e){ System.out.println("Child "+name+"interrupted:"+ e); } System.out.println("Exiting child "+name+"thread"); } } class RunnableDemo { public static void main(String args[]){ // 开启子线程 new NewThread("a"); new NewThread("b"); new NewThread("c"); try{ for(int i=5;i>0;i--){ System.out.println("Main thread:"+ i); Thread.sleep(1000); } } catch(InterruptedException e){ System.out.println("Main interrupted:"+ e); } System.out.println("Exiting Main thread"); } }
主线程必须在最后结束运行,因为对于某些旧的JVM,如果主线程在子线程完成之前结束,java运行时系统可能会挂起。
扩展Thread类:
class NewThread extends Thread{ NewThread(){ super("Demo Thread"); System.out.println("Child Thread:"+this); start(); } public void run(){ try{ for(int i =5; i>0;i--){ System.out.println("Child thread:"+ i); Thread.sleep(500); } } catch(InterruptedException e){ System.out.println("Child interrupted:"+ e); } System.out.println("Exiting child thread"); } } public class EntendThread { public static void main(String args[]){ // 开启子线程 new NewThread(); try{ for(int i=5;i>0;i--){ System.out.println("Main thread:"+ i); Thread.sleep(1000); } } catch(InterruptedException e){ System.out.println("Main interrupted:"+ e); } System.out.println("Exiting Main thread"); } }
join() 在主线程中执行子线程A.join(),则主线程会更这个子线程A执行完毕之后才会进行后续操作
class NewThread implements Runnable { Thread t; private String name; NewThread(String threadName){ name = threadName; t = new Thread(this,threadName); System.out.println("Child thread:"+ t); t.start(); } @Override public void run() { // TODO Auto-generated method stub try{ for(int i =5; i>0;i--){ System.out.println("Child thread "+name+":"+ i); Thread.sleep(500); } } catch(InterruptedException e){ System.out.println("Child "+name+"interrupted:"+ e); } System.out.println("Exiting child "+name+"thread"); } } class RunnableDemo { public static void main(String args[]){ // 开启子线程 NewThread A = new NewThread("a"); NewThread B = new NewThread("b"); NewThread C = new NewThread("c"); System.out.println("thread a is alive:"+ A.t.isAlive()); System.out.println("thread b is alive:"+ B.t.isAlive()); System.out.println("thread c is alive:"+ C.t.isAlive()); try{ System.out.println("waiting for threads to finish"); A.t.join(); B.t.join(); C.t.join(); for(int i=5;i>0;i--){ System.out.println("Main thread:"+ i); Thread.sleep(1000); } } catch(InterruptedException e){ System.out.println("Main interrupted:"+ e); } System.out.println("thread a is alive:"+ A.t.isAlive()); System.out.println("thread b is alive:"+ B.t.isAlive()); System.out.println("thread c is alive:"+ C.t.isAlive()); System.out.println("Exiting Main thread"); } }
同步
synchronized方法
class Callme{ synchronized void call(String msg){ System.out.print("["+msg); try{ Thread.sleep(1000); } catch(InterruptedException e){ System.out.println("InterruptedException"); } System.out.println("]"); } } class Caller implements Runnable{ String msg; Callme target; Thread t; public Caller(Callme targ,String s){ target= targ; msg =s; t = new Thread(this); t.start(); } @Override public void run() { // TODO Auto-generated method stub target.call(msg); } } public class Synchronized { public static void main(String args[]){ Callme target = new Callme(); Caller ob1 = new Caller(target,"hello"); Caller ob2 = new Caller(target,"Synchronized"); Caller ob3 = new Caller(target,"world"); try{ ob1.t.join(); ob2.t.join(); ob3.t.join(); } catch(InterruptedException e){ System.out.println("InterruptedException"); } } }
synchronized 语句
class Callme{ void call(String msg){ System.out.print("["+msg); try{ Thread.sleep(1000); } catch(InterruptedException e){ System.out.println("InterruptedException"); } System.out.println("]"); } } class Caller implements Runnable{ String msg; Callme target; Thread t; public Caller(Callme targ,String s){ target= targ; msg =s; t = new Thread(this); t.start(); } @Override public void run() { // TODO Auto-generated method stub synchronized(target){ target.call(msg); } } } public class Synchronized { public static void main(String args[]){ Callme target = new Callme(); Caller ob1 = new Caller(target,"hello"); Caller ob2 = new Caller(target,"Synchronized"); Caller ob3 = new Caller(target,"world"); try{ ob1.t.join(); ob2.t.join(); ob3.t.join(); } catch(InterruptedException e){ System.out.println("InterruptedException"); } } }
多线程通过将任务分隔到独立的逻辑单元来替换事件循环编程,线程还提供了第二个优点:消除了轮询检测。
为了避免轮询检测(通过重复检查某些条件的循环来判断是否执行某操作),java通过wait(),notify()及notifyAll() 提供了一种进程间通信机制。这三个方法只能在同步上下文中调用。
wait() 方法通知调用线程放弃监视器并进入休眠。直到其他一些线程进入同一个监视器并调用notify()方法或notifyAll()方法。
notify()方法唤醒调用相同对象的wait()中的线程。
notifyAll() 方法唤醒调用相同对象的wait()中的所有线程,其中一个线程将得到访问授权。
wait()方法有另一种形式,可以指定等待的时间间隔。因为线程有极小可能被假唤醒(没有调用notify,而线程被恢复), 所以应当在一个检查线程等待条件的循环中调用wait()
// 队列 class Q{ int n; boolean valueSet = false; synchronized int get(){ while(!valueSet){ try{ wait(); } catch(InterruptedException e){ System.out.println("InterruptedException"); } } System.out.println("Got: "+ n); valueSet =false; notify(); return n; } synchronized void put(int n){ while(valueSet){ try{ wait(); } catch(InterruptedException e){ System.out.println("InterruptedException"); } } this.n =n ; valueSet =true; System.out.println("Put: "+ n); notify(); } } class Producter implements Runnable{ Q q; Producter(Q q){ this.q = q; new Thread(this,"Producter").start(); } @Override public void run() { // TODO Auto-generated method stub int i = 0; while(i < 100){ q.put(i++); } } } class Consumer implements Runnable{ Q q; Consumer(Q q){ this.q = q; new Thread(this,"Consumer").start(); } @Override public void run() { // TODO Auto-generated method stub while(q.get() < 99){ } } } public class Wait2Notify { public static void main(String args[]){ Q q = new Q(); new Producter(q); new Consumer(q); } }
死锁
一个线程进入对象X的监视器,另一个线程进入对象Y的监视器,如果X中的线程试图调用对象Y的任何同步方法,那么都会被阻塞,需要等待当前占据对象Y的线程执行完毕,移出对象Y的监视器,而如果对象Y中的线程也试图调用对象X的任何同步方法,则会造成死锁。
class A{ synchronized void foo(B b){ String name = Thread.currentThread().getName(); System.out.println(name + " entered A.foo()"); try{ Thread.sleep(1000); } catch(Exception e){ System.out.println("A Interrupted"); } System.out.println(name +" trying to call B.last()"); b.last(); } synchronized void last(){ System.out.println("Inside A.last()"); } } class B{ synchronized void bar(A a){ String name = Thread.currentThread().getName(); System.out.println(name + " entered B.bar()"); try{ Thread.sleep(1000); } catch(Exception e){ System.out.println("B Interrupted"); } System.out.println(name +" trying to call A.last()"); a.last(); } synchronized void last(){ System.out.println("Inside B.last()"); } } public class DeadLock implements Runnable { A a = new A(); B b = new B(); DeadLock(){ Thread.currentThread().setName("MainThread"); Thread t = new Thread(this,"RacingThread"); t.start(); a.foo(b); System.out.println("back in main thread"); } public void run(){ b.bar(a); System.out.println("back in other thread"); } public static void main(String args[]){ new DeadLock(); } }
挂起,恢复,停止
java 2以前,程序使用Thread类定义的suspend(),resume(),stop()方法。这些方法在java2不再使用的原因是:
suspend()方法有时会导致严重的系统故障,假定线程为关键数据结构加锁,如果这是线程被挂起,那么这些锁将无法释放。其他可能等待这些资源的线程会被死锁。
resume()与suspend()是配对使用的,所以也一起废弃。
stop()方法有时也会造成严重的系统故障,假定线程正在向关键的重要数据结构中写入数据,并且只完成了部分发生变化的数据。如果这时停止线程,那么数据就够可能会处于损坏状态,问题是stop()会导致释放调用线程的所有锁,因此,另一个正在等待相同锁的线程可能会使用这些已损坏的数据。
所以现在面向函数编程开始流行,就是因为函数式编程不需要保存状态,也就不需要锁定,也就没有上述的各种状况。可以大大简化多线程操作的难度。
现在,线程被设计为run()方法周期性进行检查,以确保是否应当挂起,恢复或停止线程自身的执行。
class NewThread implements Runnable{ String name; Thread t; boolean suspendFlag; NewThread(String threadName){ name = threadName; t = new Thread(this,name); System.out.println("New Thread: "+ t); suspendFlag = false; t.start(); } @Override public void run() { // TODO Auto-generated method stub try{ for(int i = 15; i>0;i--){ System.out.println(name +": "+ i); Thread.sleep(2000); synchronized(this){ while(suspendFlag){ wait(); } } } } catch(InterruptedException e){ System.out.println(name +" InterruptedException"); } System.out.println(name +" exiting"); } synchronized void mysuspend(){ suspendFlag =true; } synchronized void myresume(){ suspendFlag =false; notify(); } } public class Mysuspend { public static void main(String args[]){ NewThread ob1 = new NewThread("one"); NewThread ob2 = new NewThread("two"); try{ Thread.sleep(1000); ob1.mysuspend(); System.out.println("Suspending thread one"); Thread.sleep(1000); ob1.myresume(); System.out.println("Resuming thread one"); ob2.mysuspend(); System.out.println("Suspending thread two"); Thread.sleep(1000); ob2.myresume(); System.out.println("Resuming thread two"); } catch(InterruptedException e){ System.out.println("Main thread InterruptedException"); } try{ System.out.println("Waiting for threads to finish"); ob1.t.join(); ob2.t.join(); } catch(InterruptedException e){ System.out.println("Main thread InterruptedException"); } System.out.println("Main thread exiting"); } }
线程状态: if(t.getState() == Thread.State.RUNNABLE)
BLOCKED/*因为正在等待需要的锁儿挂起执行*/,
NEW/*线程换没有开始执行*/,
RUNNABLE/*线程要么当前正在运行,要么在获取cpu的访问权后执行*/,
TERMINATED/*线程已经完成执行*/,
TIMED_WAITING/*线程挂起执行一段指定的时间,如sleep(),暂停版的wait(),join()*/,
WAITING/*线程因为某些动作而挂起执行,如调用非暂停版的wait(),join()*/
线程的上下文切换需要一定的开销,如果创建的线程太多,可能反而会降低程序的性能。 Fork/Join 框架创建可自动伸缩的计算密集型应用程序
枚举:
从jdk 5.0开始枚举 被添加到java语言中, 不同的是,java的枚举是用类实现的。所以可以具有构造函数,方法及实例变量,甚至实现接口。
enum Apple{ //枚举常量 被隐式声明为Apple的 public static final 成员,其类型声明为枚举的类型。 Jonathan,GoldenDel,RedDel,Winesap,Cortland } public class EnumDemo { Apple ap = Apple.Cortland; // 比较相等性用 == boolean test(Apple a){ // 会显示名称 Jonathan System.out.println(a); return a == ap; } void switchTest(){ switch(ap){ case Jonathan: // ... case GoldenDel: // ... default: // ... } } }
所有枚举自动包含两个预定义方法 values() 和 valueOf()
enum Apple{ //枚举常量 被隐式声明为Apple的 public static final 成员,其类型声明为枚举的类型。 Jonathan,GoldenDel,RedDel,Winesap,Cortland } public class EnumDemo { public static void main(String args[]){ Apple ap; System.out.println("Here are all Apple constants"); Apple allApples[] = Apple.values(); for(Apple a : allApples){ System.out.println(a); } ap = Apple.valueOf("GoldenDel"); System.out.println("ap contains " + ap); } }
// java枚举的类特性 enum Apple{ // 实例对象 Jonathan(10),GoldenDel(9),RedDel,Winesap(15),Cortland(8); // 变量 private int price; // 构造函数 Apple(int p){ price = p; } Apple(){ price = -1; } // 方法 int getPrice(){ return price; } } class EnumDemo{ public static void main(String args[]){ Apple ap = Apple.Winesap; System.out.println("Winesap costs "+ ap.getPrice() + " cents.\n"); System.out.println("All apple prices"); for (Apple a : Apple.values()){ if (a.compareTo(ap) < 0){ System.out.println(a.ordinal() +" : " +a+" costs "+ a.getPrice()+ " cents."+ a + " before "+ ap); } else if (a.compareTo(ap) > 0){ System.out.println(a.ordinal() +" : " +a+" costs "+ a.getPrice()+ " cents."+ a + " after "+ ap); } else { System.out.println(a.ordinal() +" : " +a+" costs "+ a.getPrice()+ " cents."+ a + " equal "+ ap); } } } }
枚举不能继承其他类,因为所有枚举都自动继承超类 java.lang.Enum, 虽然同样具有面向对象的特性,但java的枚举与swift差别很大。
ordinal() 可以获取枚举常量在常量列表里的位置的值(序数值 0,1,2 ...)
compareTo 可以比较相同类型的两个枚举常量的序数值。
equals() 方法 等同 ==
注解(元数据) :
从jdk5 开始,java支持在源文件中嵌入补充信息,称为注释(annotation)。注释不会改变程序的动作,也就不会改变程序的语义。但在开发和部署期间,各种工具可以使用这类信息。元数据(metadata)也用于表示这一特性。
设计注释的主要目的是用于其他的开发和部署工具,但是如果为注释指定为RUNTIME保留策略,那么任何程序在运行时都可以使用反射来查询注释。反射时能够在运行时获取类相关信息的特性(java.lang.reflect)。
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; //@interface声明一个注解类型,注解类型内部有方法声明,不能使用extends子句 //所有注解类型都自动扩展了Annotation接口(java.lang.annotation,其中重写了hashCode(),equals(),toString() //还指定了annotationType()方法) //保留策略:SOURCE/*只在源文件保留,编译时会被抛弃*/ // CLASS /*在编译时存储到.class文件,但运行时通过JVM不能得到这些注释*/ // RUNTIME /*在编译时存储到.class文件,并且运行时可以通过JVM获取这些注释*/ @Retention(RetentionPolicy.RUNTIME) @interface MyAnno{ String str(); int val(); } class Meta { // 应用注释时,需要为注释的成员提供值。注释的成员看起来像域变量 @MyAnno(str = "Annotation Example",val = 100) public static void myMeth(String str,int i){ Meta ob = new Meta(); try{ // 获取Class对象,之后可以获取与类声明中各个条目相关的信息,包括注释 Class<?> c = ob.getClass(); // getMethod,getField,getConstructor Method m = c.getMethod("myMeth",String.class,int.class); // 对Class,Method,Field,Constructor对象调用getAnnotation,可以获得与对象关联的特定注释。 // 如果没有注释或保留策略不是RUNTIME,则返回null // MyAnno.class称为 类字面值,当需要已知类的Class对象时,可以用这个表达式:Class<?> c = Meta.class; MyAnno anno = m.getAnnotation(MyAnno.class); System.out.println(anno.str() + " : "+ anno.val()); } catch(NoSuchMethodException e){ System.out.println("NoSuchMethodException"); } } public static void main(String args[]){ myMeth("test",10); } }
import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; @Retention(RetentionPolicy.RUNTIME) @interface MyAnno{ String str(); int val(); } @Retention(RetentionPolicy.RUNTIME) @interface What{ // 默认值 String description() default "Testing"; } @What(description = "An annotation test class") @MyAnno(str = "Meta2", val = 99) class Meta2 { @What(description = "An annotation test method") @MyAnno(str = "Testing", val = 100) public static void myMeth(){ Meta2 ob = new Meta2(); try{ // 获取Class对象,之后可以获取与类声明中各个条目相关的信息,包括注释 Annotation[] annos = ob.getClass().getAnnotations(); System.out.println("All annotations for Meta2"); for (Annotation a:annos){ System.out.println(a); } System.out.println(); Method m = ob.getClass().getMethod("myMeth"); annos = m.getAnnotations(); System.out.println("All annotations for myMeth"); for (Annotation a:annos){ System.out.println(a); } } catch(NoSuchMethodException e){ System.out.println("NoSuchMethodException"); } } public static void main(String args[]){ myMeth(); } }
单成员注解或其他成员有默认值时,可以使用缩写形式(成员名称必须为value),
@Rentetion(RentetionPolicy.RUNTIME) @interface MySingle{ int value(); int xyz() default 0; } class Single{ @MySingle(88) public static void myMeth(){ } }
getAnnotation(),getAnnotations()由AnnotatedElement接口(java.lang.reflect)定义,
该接口支持注解反射,类Method,Field,Constructor,Class,Package都实现了这个接口。
getDeclaredAnnotations() 返回调用对象中存在的所有非继承注解
isAnnotationPresent(Class<? extends Annotation> annoType) annoType指定的注解与调用对象是否相关联
jdk 8新增:getDeclaredAnnotation() ,getAnnotationsByType()和getDeclaredAnnotationsByType(),后两个方法自动使用重复注释。
标记注解:
标记注解是特殊类型的注解,其中不包含成员,唯一目的是标记声明,
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; @Retention(RetentionPolicy.RUNTIME) @interface MyMarker{} class Marker { @MyMarker public static void myMeth(){ Marker ob =new Marker(); try { Method m = ob.getClass().getMethod("myMeth"); if (m.isAnnotationPresent(MyMarker.class)){ System.out.println("MyMarker is present"); } } catch (NoSuchMethodException | SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String args[]){ myMeth(); } }
内置注解:
java提供了许多内置注解,大部分为专有注解,
但有9个用于一般目的。
4个来自 java.lang.annotation, 只能注解其他注解
@Retention /*指定保留策略*/,
@Documented /*标记注解,用于通知某个工具——注解将被文档化*/,
@Target /*用于指定可以应用注解的声明的类型*/,
@Target(ElementType.FIELD), @Target({ElementType.FIELD,ElementType.LOCAL_VARIABLE})
@Inherited /*标记注解,只影响用于类声明的注解,会导致超类的注解被子类继承。*/
5个来自java.lang:
@Override /*标记注解,只能用于方法,带有此注解的方法必须重写超类中的方法,否则会编译报错。用于确保超类方法呗真正地重写,而不是简单地重载。*/
@Deprecated/*标记注解,用于指示声明是过时的,并且已经被更新的形式取代*/
@FunctionalInterface/*标记注解,jdk 8 新增,用于接口指出被注释的接口是一个函数式接口*/
@SafeVarargs /*标记注解,只能用于方法和构造函数,指示没有发生与可变长度参数相关的不安全操作。用于移植“未检查不安全代码”警告。只能用于varargs方法或者 声明为static或final的构造函数*/
@SuppressWarnings/*用于指定能抑制一个或多个编译器可能会报告的警告,使用字符串形式表示的名称来制定要被抑制的警告*/
类型注解
从 jdk 8开始,java增加了可以使用注解的地方,如前面的例子,早期的注解只能用于声明。但丛jdk 8开始在能够使用类型的大部分地方,也可以指定注解。扩展后的这种注解 称为 类型注解。
类型注解 可以注解 方法的返回类型,方法内this的类型,强制转换,数组级别,被继承的类及throws子句,泛型。
类型注解很重要,因为它们允许工具对代码执行额外的检查。
类型注解必须包含ElementType.TYPE_USE作为目标。
jdk 8 新增 TYPE_PARAMETER, ElementType.TYPE_USE
import java.lang.annotation.*; import java.lang.reflect.*; // 类型注解 @Target(ElementType.TYPE_USE) @interface TypeAnno{} // 类型注解 @Target(ElementType.TYPE_USE) @interface NotZeroLen{} // 类型注解 @Target(ElementType.TYPE_USE) @interface Unique{} // 类型注解 @Target(ElementType.TYPE_USE) @interface MaxLen{ int value(); } // 非类型注解,用于注解泛型参数声明 @Target(ElementType.TYPE_PARAMETER) @interface What{ String description(); } // 标记注解 @Target(ElementType.FIELD) @interface EmptyOK{} // 标记注解 @Target(ElementType.METHOD) @interface Recommended{} public class TypeAnnoDemo<@What(description = "Generic data type") T> { // 用于构造函数的类型注解 public @Unique TypeAnnoDemo(){} // 类型注解 用于注解类型,而不是字段 @TypeAnno String str; // 注解字段 test @EmptyOK String test; // 注解 this(接收方),this是所有实例方法的隐式参数,它引用的是调用对象。 // 丛jdk 8开始,可以显式地将this声明为方法的第一个参数,在这种声明里,this的类型必须是其类的类型 public int f(@TypeAnno TypeAnnoDemo<T> this,int x){ return 10; } // 注解返回类型 public @TypeAnno Integer f2(int j,int k){ // 自动封装 return j + k; } // 注释 方法声明 public @Recommended Integer f3(String str){ return str.length(); } // 注解 throws 子句 public void f4() throws @TypeAnno NullPointerException{ // ... } // 注解数组级别 String @MaxLen(10) [] @NotZeroLen [] w; // 注解数组元素类型 @TypeAnno Integer[] vec; // 注释 extends 子句 class SomeClass extends @TypeAnno TypeAnnoDemo<Boolean>{} public static void myMeth(int i){ // 注解类型参数 TypeAnnoDemo<@TypeAnno Integer> ob = new TypeAnnoDemo<@TypeAnno Integer>(); // 注解 @Unique TypeAnnoDemo<Integer> ob2 = new @Unique TypeAnnoDemo<Integer>(); Object x = new Integer(10); Integer y; // 注解 类型转换 y = (@TypeAnno Integer) x; } public static void main(String args[]){ myMeth(10); } }
重复注释 :jdk 8 新增的另一个注解特性 是允许在相同元素上重复应用注释。
可重复的注释 必须用@Repeatable 进行注解。
import java.lang.annotation.*; import java.lang.reflect.*; @Retention(RetentionPolicy.RUNTIME) // 指定重复注解的容器类型 @Repeatable(MyRepeatedAnnos.class) @interface MyAnno{ String str() default "Testing"; int val() default 9000; } // 容器 @Retention(RetentionPolicy.RUNTIME) @interface MyRepeatedAnnos{ // value是重复注解类型的数组 MyAnno[] value(); } public class RepeatAnno { @MyAnno(str = "First annotation", val = -1) @MyAnno(str = "Second annotation", val = 100) public static void myMeth(String str,int i){ RepeatAnno ob = new RepeatAnno(); Class<?> c = ob.getClass(); try { Method m = c.getMethod("myMeth", String.class,int.class); // 使用getAnnotation Annotation anno = m.getAnnotation(MyRepeatedAnnos.class); System.out.println(anno); // 使用jdk 8 新增的方法 Annotation[] annos = m.getAnnotationsByType(MyAnno.class); for(Annotation a : annos){ System.out.println(a); } } catch (NoSuchMethodException | SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String args[]){ myMeth("test",100); } }
注解的限制:
一个注解不能继承另一个注解。
注解声明的方法都不带参数。
注解不能泛型化。
注解方法不能指定throws子句。
类型注解被用来支持在Java的程序中做强类型检查。配合插件式的check framework,可以在编译的时候检测出runtime error,以提高代码质量。
check framework是第三方工具,配合Java的类型注解效果就是1+1>2。它可以嵌入到javac编译器里面,可以配合ant和maven使用,也可以作为eclipse插件。地址是http://types.cs.washington.edu/checker-framework/。
check framework可以找到类型注解出现的地方并检查,举个简单的例子:
import checkers.nullness.quals.*; public class GetStarted { void sample() { @NonNull Object ref = new Object(); } }
使用javac编译上面的类
编译是通过,但如果修改成:
@NonNull Object ref = null;
如果你不想使用类型注解检测出来错误,则不需要processor,直接javac GetStarted.java是可以编译通过的,这是在java 8 with Type Annotation Support版本里面可以,但java 5,6,7版本都不行,因为javac编译器不知道@NonNull是什么东西,但check framework 有个向下兼容的解决方案,就是将类型注解nonnull用/**/注释起来
,比如上面例子修改为:
import checkers.nullness.quals.*; public class GetStarted { void sample() { /*@NonNull*/ Object ref = null; } }
这样javac编译器就会忽略掉注释块,但用check framework里面的javac编译器同样能够检测出nonnull错误。
通过类型注解+check framework我们可以看到,现在runtime error可以在编译时候就能找到。
深入理解Java:注解(Annotation)--注解处理器
I/O :
通过流执行 I/O,流是一种抽象,流通过java的I/O系统链接到物理设备。所有流的行为方式都是相同的。可以将不同的输入设备(磁盘文件,键盘,网络socket抽象为输入流),对应的输出流可以引用控制台,磁盘文件,网络连接。
在java.io中定义了基于流的i/o,在java.nio中还定义了基于缓冲和基于通道的i/o
java定义了两种流:字节流(在最底层,所有i/o仍然是面向字节的)和字符流(使用unicode编码,便于处理字符)
泛型 :
jdk 5引入了泛型,泛型可以只定义算法一次,使其独立于特定的数据类型,然后将算法应用于各种数据类型而不需要任何额外的工作。
泛型为语言增加的强大功能从根本上改变了代码的编写方式。
泛型本质上是提供类型的"类型参数",它们也被称为参数化类型(parameterized type)或参量多态(parametric polymorphism)。
在引入泛型特性之前,java是通过Object类型的引用变量来操作各种类型的对象,但它们不能以类型安全的方式进行工作。因为需要显式地使用强制类型转换。
// ..... package N; public interface Iterator{ public Object next (); public boolean hasNext (); } // ..... package N; public interface Collection{ public void add(Object x); public Iterator iterator(); } // ..... package N; public class NoSuchElementException extends RuntimeException { } // ..... package N; public class MyLinkedList implements Collection { protected class Node{ Object elt; Node next = null; Node (Object elt) { this.elt = elt; } } protected Node head = null, tail = null; public MyLinkedList () {} @Override public void add(Object elt) { // TODO Auto-generated method stub if (head == null) { head = new Node(elt); tail = head; } else { tail.next = new Node(elt); tail = tail.next; } } @Override public Iterator iterator() { // TODO Auto-generated method stub return new Iterator () { protected Node ptr = head; public boolean hasNext () { return ptr != null; } public Object next () { if (ptr != null) { Object elt = ptr.elt; ptr = ptr.next; return elt; } else { throw new NoSuchElementException (); } } }; } } // ..... import N.*; public class Test { public static void main(String[] args) { MyLinkedList xs = new MyLinkedList(); xs.add(new Byte((byte) 0)); xs.add(new Byte((byte) 1)); Iterator xi = xs.iterator(); while(xi.hasNext()){ Byte x = (Byte)xi.next(); System.out.println("Byte:"+ x); } System.out.println(); MyLinkedList ys = new MyLinkedList(); ys.add("zero"); ys.add("one"); Iterator yi = ys.iterator(); while(yi.hasNext()){ String y = (String) yi.next(); System.out.println("String:"+ y); } System.out.println(); MyLinkedList zs = new MyLinkedList(); zs.add(ys); zs.add(ys); Iterator zi = zs.iterator(); while(zi.hasNext()){ MyLinkedList zlist = (MyLinkedList)zi.next(); Iterator zzi = zlist.iterator(); while(zzi.hasNext()){ String y = (String) zzi.next(); System.out.println("String:"+ y); } } Byte w = (Byte)ys.iterator().next(); // run-time exception System.out.println("Byte:"+ w); } }
上面的例子,编译器不知道Object的实际类型,无法发现强制类型转换的错误,需要到运行时才会抛出。这一缺陷推迟了发现错误的时间。使用泛型实现,则可以在编译时就发现错误。
package T; public interface Collection<T> { public void add(T x); public Iterator<T> iterator(); } // ... package T; public interface Iterator<T> { public T next(); public boolean hasNext(); } // ... package T; import N.NoSuchElementException; public class MyLinkedList<T> implements Collection<T> { protected class Node{ Node next = null; T elt; Node(T elt){ this.elt =elt; } } protected Node head = null,tail = null; public MyLinkedList () {} @Override public void add(T x) { // TODO Auto-generated method stub if(head == null){ head = new Node(x); tail = head; } else{ tail.next = new Node(x); tail = tail.next; } } @Override public Iterator<T> iterator() { // TODO Auto-generated method stub return new Iterator<T> (){ protected Node ptr = head; @Override public T next() { // TODO Auto-generated method stub if (ptr != null) { T elt = ptr.elt; ptr = ptr.next; return elt; } else { throw new NoSuchElementException (); } } @Override public boolean hasNext() { // TODO Auto-generated method stub return ptr != null; } }; } } // ... import T.*; public class Test { public static void main(String[] args) { MyLinkedList<Byte> xs = new MyLinkedList<Byte>(); xs.add(new Byte((byte) 0)); xs.add(new Byte((byte) 1)); Iterator<Byte> xi = xs.iterator(); while(xi.hasNext()){ Byte x = xi.next(); System.out.println("Byte:"+ x); } System.out.println(); MyLinkedList<String> ys = new MyLinkedList<String>(); ys.add("zero"); ys.add("one"); Iterator<String> yi = ys.iterator(); while(yi.hasNext()){ String y = yi.next(); System.out.println("String:"+ y); } System.out.println(); MyLinkedList<MyLinkedList<String>> zs = new MyLinkedList<MyLinkedList<String>>(); zs.add(ys); zs.add(ys); Iterator<MyLinkedList<String>> zi = zs.iterator(); while(zi.hasNext()){ MyLinkedList<String> zlist = zi.next(); Iterator<String> zzi = zlist.iterator(); while(zzi.hasNext()){ String y = zzi.next(); System.out.println("String:"+ y); } } } }
String 是 Object 的子类型,因此,我们可以将 String 类型的变量赋值给 Object 类型的变量,甚至可以将 String [ ] 类型的变量(数组)赋值给 Object [ ] 类型的变量,即 String [ ] 是 Object [ ] 的子类型。 但这一特性不适用于泛型。
List<String> ls = new ArrayList<String>(); List<Object> lo = ls; // 报错,破坏了泛型 的类型安全 lo.add(new Integer()); String s = ls.get(0);
受限的类型参数(有界类型)
// 在指定参数类型时,可以声明超类的上界,默认上界是Object // 要调用doubleValue(),需要声明 类型参数T必须派生自Number // 当需要指定具有一个类和多个接口的边界时,使用 & 运算符连接 它们 // class Gen(T extends MyClass & MyInterface & MyInterface2 {} public class Stats<T extends Number> { T[] nums; Stats(T[] o){ nums = o; } double average(){ double sum = 0.0; for(int i =0;i<nums.length;i++){ sum += nums[i].doubleValue(); } return sum / nums.length; } boolean sameAvg(Stats<T> ob){ if(average() == ob.average()){ return true; } return false; } }
通配符参数 :
上例的sameAvg()只有比较双方的类型相同时才能工作,不能比对 Stats<Double>与Stats<Integer>
// 使用通配符参数 ? boolean sameAvg(Stats<?> ob){ if(average() == ob.average()){ return true; } return false; }
上例通配符只是简单地匹配所有有效的Stats对象,范围由Stats声明中的extends子句控制。
而如同有界参数一样,通配符也可以增加限制,不同在于有界通配符不仅可以有上界<? extends superclass>,也可以有下界<? super subclass>
class TwoD{ int x,y; TwoD(int a,int b){ x = a; y =b; } } class ThreeD extends TwoD{ int z; ThreeD(int a,int b, int c){ super(a, b); z = c; } } class FourD extends ThreeD{ int t; FourD(int a, int b, int c,int d) { super(a, b, c); t = d; } } class Coords<T extends TwoD>{ T[] coords; Coords(T[] o){ coords = o; } } public class BoundedWildcards { static void showXY(Coords<?> c){ System.out.println("X Y Coordinates:"); for (int i = 0; i < c.coords.length; i++) { System.out.println(c.coords[i].x + " " + c.coords[i].y); } System.out.println(); } static void showXYZ(Coords<? extends ThreeD> c){ System.out.println("X Y Z Coordinates:"); for (int i = 0; i < c.coords.length; i++) { System.out.println(c.coords[i].x + " " + c.coords[i].y+ " " + c.coords[i].z); } System.out.println(); } static void showAll(Coords<? extends FourD> c){ System.out.println("X Y Z T Coordinates:"); for (int i = 0; i < c.coords.length; i++) { System.out.println(c.coords[i].x + " " + c.coords[i].y+ " " + c.coords[i].z+ " " + c.coords[i].t); } System.out.println(); } public static void main(String[] args) { TwoD td[] = { new TwoD(0, 0), new TwoD(7, 9), new TwoD(18, 4), new TwoD(-1, -23), new TwoD(22, 44) }; Coords<TwoD> tdlos = new Coords<>(td); System.out.println("Contens of tdlos:"); showXY(tdlos); // 编译报错 //showXYZ(tdlos); //showAll(tdlos); FourD fd[] = { new FourD(1, 2, 3, 4), new FourD(6, 8, 9, 14), new FourD(22, 9, 23, 4), new FourD(3, -2, 53, -4), }; Coords<FourD> fdlos = new Coords<>(fd); System.out.println("Contens of fdlos:"); showXY(fdlos); showXYZ(fdlos); showAll(fdlos); } }
public interface Stream<T> extends BaseStream<T, Stream<T>> { Stream<T> filter(Predicate<? super T> predicate); <R> Stream<R> map(Function<? super T, ? extends R> mapper); IntStream mapToInt(ToIntFunction<? super T> mapper); LongStream mapToLong(ToLongFunction<? super T> mapper); DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper); <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper); // ... }
前面的是泛型类,泛型参数
泛型方法的格式 public <T extends Comparable<T>, V extends T> boolean isIn(T x, V[] y){}
public class GenMethDemo { static <T extends Comparable<T>,V extends T> boolean isIn(T x, V[] y){ for (int i = 0; i < y.length; i++) { if(x.equals(y[i])){ return true; } } return false; } public static void main(String[] args) { Integer nums[] ={ 1,2,3,4,5 }; // 完整形式 GenMethDemo.<Integer,Integer>isIn(2,nums),但对大多数泛型方法,类型推断就足够了 if(isIn(2,nums)){ System.out.println("2 is in nums"); } System.out.println(); String strs[] ={ "one","two","three","four","five" }; if(isIn("two",strs)){ System.out.println("two is in strs"); } } }
泛型构造函数; 可以将构造函数泛型化,即使它们的类不是泛型类
class GenericConstructor { private double val; <T extends Number> GenericConstructor(T arg){ val = arg.doubleValue(); } void showVal(){ System.out.println("val: "+ val); } } class GenConDemo{ public static void main(String[] args) { GenericConstructor test1 = new GenericConstructor(100); GenericConstructor test2 = new GenericConstructor(123.5F); test1.showVal(); test2.showVal(); } }
泛型接口:使用接口作为泛型的上界时,也用extends, 具体形式与泛型类相同
package T; public interface Comparable<T> { int compareTo(T that); } // ... package T; public class MyByte implements Comparable<MyByte> { private byte value; public MyByte(byte value) {this.value = value;} public byte byteValue() {return value;} @Override public int compareTo(MyByte that) { return this.value - that.value; } public String toString(){ return "" + value; } } // ... package T; public class Collections { public static <A extends Comparable<A>> A max (Collection<A> xs){ Iterator<A> xi = xs.iterator(); A w = xi.next(); while (xi.hasNext()) { A x = xi.next(); if (w.compareTo(x) < 0) w = x; } return w; } } // ... import T.*; public class Test { public static void main(String[] args) { MyLinkedList<MyByte> xxs = new MyLinkedList<MyByte>(); xxs.add(new MyByte((byte) 3)); xxs.add(new MyByte((byte) 4)); Iterator<MyByte> xxi = xxs.iterator(); while(xxi.hasNext()){ MyByte x = xxi.next(); System.out.println("MyByte:"+ x); } MyByte xx = Collections.max(xxs); System.out.println("max MyByte:"+ xx); System.out.println("type of xx is: "+ xx.getClass().getName()); } }
// public interface Comparable<T> {} interface MinMax<T extends Comparable<T>>{ T min(); T max(); } class MyClass<T extends Comparable<T>> implements MinMax<T>{ T[] vals; MyClass(T[] o){ vals = o; } @Override public T min() { if (vals.length == 0)return null; T v = vals[0]; for (int i = 0; i < vals.length; i++) { if(vals[i].compareTo(v)< 0) { v= vals[i]; } } return v; } @Override public T max() { if (vals.length == 0)return null; T v = vals[0]; for (int i = 0; i < vals.length; i++) { if(vals[i].compareTo(v)> 0) { v= vals[i]; } } return v; } } public class GenericInterface { public static void main(String[] args) { Integer inums[]={ 3,6,2,8,6 }; Character chs[] ={ 'b','r','p','w' }; MyClass<Integer> iob = new MyClass<>(inums); MyClass<Character> cob = new MyClass<>(chs); System.out.println("Max value in inums is :" + iob.max()); System.out.println("Min value in inums is :" + iob.min()); System.out.println("Max value in chs is :" + cob.max()); System.out.println("Min value in chs is :" + cob.min()); } }
因为java的泛型只支持引用类型,不支持基本数据类型。所以jdk 5提供的自动装箱与自动拆箱,可以极大地简化代码,
而从jdk 7 开始,可以进一步缩短创建泛型实例的语法。
MyClass<Integer,String> mcOb = new MyClass<Integer,String>(new Integer(88),"A String"); MyClass<Integer,String> mcOb = new MyClass<Integer,String>(88,"A String"); MyClass<Integer,String> mcOb = new MyClass<>(88,"A String");
泛型类作为超类和子类时,在泛型层次中,所有子类都必须向上传递超累所需的所有类型参数。与沿类层次向上传递构造函数的参数类似。
泛型层次中的 运行时类型比较 及强制转换
class Gen<T>{ T ob; Gen(T o){ ob = o; } } class Gen2<T> extends Gen<T>{ Gen2(T o){ super(o); } } class GenInstanceof { public static void main(String[] args) { Gen<Integer> iOb = new Gen<>(88); Gen2<Integer> iOb2 = new Gen2<>(99); Gen2<String> strOb2 = new Gen2<>("Generics Test"); if(iOb2 instanceof Gen2<?>){ System.out.println("iobs is instance of Gen2"); } if(iOb2 instanceof Gen<?>){ // 强制类型转换,因为iOb2是Gen<Integer>的实例 Gen<Integer> iObcast = (Gen<Integer>) iOb2; System.out.println("iobs is instance of Gen"); } if(strOb2 instanceof Gen2<?>){ System.out.println("strOb2 is instance of Gen2"); } if(strOb2 instanceof Gen<?>){ System.out.println("strOb2 is instance of Gen"); } System.out.println(); if(iOb instanceof Gen2<?>){ System.out.println("iOb is instance of Gen2"); } else{ System.out.println("iOb is not instance of Gen2"); } if(iOb instanceof Gen<?>){ System.out.println("iOb is instance of Gen"); } //不能编译通过,因为在运行时不能使用泛型类型信息。 // if(iOb2 instanceof Gen<Integer>){ // System.out.println("iOb2 is instance of Gen<Integer>"); // } } }
泛型 与 c++ 中的模板很类似,但二者处理泛型类型的方式有本质区别
在 C++ 模板中,编译器使用提供的类型参数来扩充模板,类似字符替换的过程。
而在java中,影响泛型实现方式的一个重要约束就是需要与以前的JAVA版本兼容。为此JAVA才用"擦除"实现泛型。java虚拟机是不支持泛型的,java的泛型会在编译过程中,首先转化为不带泛型的普通java程序。所以在运行时没有类型参数,它们只是一种源代码机制。转化过程中包含几个部分:
/* class Gen<T>{ T ob; Gen(T o){ ob = o; } T getOb(){ return ob; } } */ class Gen{ Object ob; Gen(Object o){ ob =o; } Object getOb(){ return ob; } } /* class Gen2 extends Gen<String>{ Gen2(String o) { super(o); } } */ class Gen2 extends Gen{ Gen2(String o) { super(o); } } class GenericBridgeDemo { public static void main(String[] args) { /* Gen2 strOb2 = new Gen2("Generics Test"); String ob = strOb2.getOb(); System.out.println(ob); */ Gen2 strOb2 = new Gen2("Generics Test"); String ob = (String) strOb2.getOb(); System.out.println(ob); } }
/* interface Comparable<T extends Number> { int compareTo(T that); } */ interface Comparable { int compareTo(Number that); } /* class MyByte extends Number implements Comparable<MyByte> { private static final long serialVersionUID = 2L; private byte value; public MyByte(byte value) {this.value = value;} public byte byteValue() {return value;} public int intValue() {return value;} public long longValue() {return value;} public float floatValue() {return value;} public double doubleValue() {return value;} public int compareTo(MyByte that) { return this.value - that.value; } public String toString(){ return "" + value; } } */ class MyByte extends Number implements Comparable { private static final long serialVersionUID = 2L; private byte value; public MyByte(byte value) {this.value = value;} public byte byteValue() {return value;} public int intValue() {return value;} public long longValue() {return value;} public float floatValue() {return value;} public double doubleValue() {return value;} public int compareTo(MyByte that) { return this.value - that.value; } public String toString(){ return "" + value; } // 为了可以正常覆盖超类和接口的方法,引入了桥方法 public int compareTo(Number that) { return this.compareTo((MyByte)that); } } class GenericBridgeDemo{ public static void main(String[] args) { MyByte a = new MyByte((byte)88); MyByte b = new MyByte((byte)77); MyByte c = new MyByte((byte)77); MyByte d = new MyByte((byte)99); System.out.println(a.intValue()); System.out.println(a.doubleValue()); if (a.compareTo(b) > 0){ System.out.println("a > b"); } if (a.compareTo(d) < 0){ System.out.println("a < d"); } if (b.compareTo(c) == 0){ System.out.println("b == c"); } } }
/* class Gen2 extends Gen<String>{ Gen2(String o) { super(o); } String getOb(){ System.out.println("调取 gen2.getOb()"); return ob; } } */ class Gen2 extends Gen{ Gen2(String o) { super(o); } String getOb(){ System.out.println("调取 gen2.getOb()"); return (String) ob; } //在 JVM 中,方法定义时所使用的方法签名包括方法的返回类型,所以覆盖需要一个桥方法 Object getOb(){ return this.getOb(); } }
java泛型的优点:
区别:
// 这个可以编译。不过使用这个函数的时候,T必须是包含bar函数的类 template <typename T> void foo(T t) { t.bar(); } // java 则不行,因为编译器只知道T是一种Object,不知道T的具体类型 static <T> void foo(T t) { t.bar(); }
模糊性错误
class MyGenClass<T,V extends Number>{ T ob1; V ob2; MyGenClass(T o1,V o2){ ob1 = o1; ob2 = o2; } void set(T o){ ob1 = o; } // 通过编译但存在模糊性错误,这里最好使用不同的方法名 void set(V o){ ob2 = o; } public String toString(){ return ob1+","+ ob2; } } public class GenericsError { public static void main(String[] args) { MyGenClass<Integer,Integer> iob = new MyGenClass<>(1,2); iob.set(12); System.out.println(iob); } }
限制:
1. 不能创建类型 为参数类型 的实例方法 ob = new T(); T vals[] = new T[10];
2. 静态成员不能使用在类中声明的类型参数 static T ob; Static T getOb();
3. 不能创建特定类型的泛型引用数组
Gen<Integer> gens[] = new Gen<Integer>[10]; // wrong
Gen<?> gens[] = new Gen<?>[10] //ok
4. 同上 运行时比较:
if( iob instanceof Gen<Integer>){} // wrong
if(iob instanceof Gen<?>){}
5. 泛型不能扩展Throwable,即不能创建泛型异常类
lambda表达式 :
java发展过程中从根本上改变代码编写方式的两次变更:jdk 5增加的泛型 和 jdk 8增加的 lambda表达式
lambda显著增强java,原因有两点:
1. 它增加了语法元素,使java的表达能力得以提升,并流线化了一些常用结构的实现方式。
2. 导致api库中增加了新功能,包括利用多核环境的并行处理功能(尤其是处理for-each风格的操作时)变得更加容易。以及支持对数据执行管道操作的新的流api。也催生了其他新的java功能,包括方法引用和接口的默认方法。
------------------------------------------------------
lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包
闭包是一个代码块,一个函数,它可以捕获其上下文中任意的变量和常量,延长其的生存周期以供自己使用,把函数以及这些变量包起来,而可以独立完成一个完整的功能。同时它如同普通数据类型的数据一样可以做参数,可以做返回值,参与了其他代码模块的构建。
——————
java的匿名内部类(没有类名,实例化后立即执行,只有一个实例,也不可被扩展,而且使用内联的方式使代码出现在使用它的位置。) 基本实现了闭包的功能,一直被称作JAVA的闭包。
匿名内部类的 捕获策略: 局部变量 必须是final,原因:
用函数实现闭包,需要提高函数的地位,JAVA坚持了它的面向对象的纯粹性,依然是只有class 为 first class, 使用简单类代替函数。
用函数实现,捕获的变量会加入参数列表,而函数的参数可以传递指针。类中变量的传入则是依靠构造函数,在匿名类内部建立同名的field,而为了保持内外的一致性,只好全部声明为final。
所以匿名内部类实现的闭包,使用起来似乎没有C#,SWIFT那样灵活了,不过考虑到java中基本全是类实现,所以这一限制其实也不算什么问题,不便的是基本数据类型,如果要使用的话,需要借助数组等形式,封装器值的变更实际是在构造新的实例,所以不适用。
但同时它传递的是一个类,不像函数式闭包只能传一个函数,它可以传任意个。虽然可能牺牲了可读性,但在匿名内部类中不光可以实现接口中定义的任意个方法,甚至可以自行添加字段,方法等元素。
import java.util.ArrayList; import java.util.List; public class Nest { List<Runnable> getList2(int n){ List<Runnable> actions = new ArrayList<Runnable>(); final int x[] = new int[1]; x[0] = 0; for (; x[0] < n; x[0]++) { actions.add( new Runnable(){ public void run() { System.out.println(x[0]); } } ); } return actions; } List<Runnable> getList(int n){ List<Runnable> actions = new ArrayList<Runnable>(); for (int counter=0; counter < n; counter++) { final int copy = counter; actions.add( new Runnable(){ public void run() { System.out.println(copy); } } ); // Local variable copy defined in an enclosing scope must be final or effectively final // copy = 10; } return actions; } public static void main(String[] args) { Nest ccc = new Nest(); List<Runnable> actions = ccc.getList(15); print(actions); System.out.println(); actions = ccc.getList2(15); print(actions); } static void print(List<Runnable> actions){ for (Runnable action : actions) { action.run(); } } }
实现机制
import java.util.ArrayList; import java.util.List; public class Nest { List<Runnable> getList(int n){ List<Runnable> actions = new ArrayList<Runnable>(); for (int counter=0; counter < n; counter++) { final int copy = counter; class temp implements Runnable{ final int copy; temp(int copy){ this.copy = copy; } public void run() { System.out.println(copy); } } actions.add(new temp(copy)); } return actions; } public static void main(String[] args) { Nest ccc = new Nest(); List<Runnable> actions = ccc.getList(15); print(actions); } static void print(List<Runnable> actions){ for (Runnable action : actions) { action.run(); } } }
java的 lambda表达式 就是一个实现单一方法的匿名内部类的简化版。使用lambda表达式 代码变得简洁,可读性提高,代码量也大大减少。
// 匿名内部类 button.addActionListener( new ActionListener(){ public void actionPerformed(ActionEventae){ System.out.println("Actiondetected"); } } ); 使用Lambda: button.addActionListener( ()->{ System.out.println("Actiondetected"); } );
不便:
1 . 用函数实现闭包,函数自身的参数列表和返回值类型本身就自解释了自身的类型。而JAVA的lambda表达式是一个类,它无法独立存在, 需要一个函数式接口来定义一个方法(规定函数的类型),使得代码依然比函数实现要复杂。 jdk 8 同时增加了java.util.funciton包,定义了许多常用的函数式接口。
2. 局部变量只能捕获 final 修饰符修饰的变量。。
3. 类实现的闭包不易扩展,使用也需要多一步操作。
let newBlock = ()->{ if(some){ block() } }
函数式接口是仅包含一个抽象方法(有默认行为的方法不算抽象方法)的接口。即通常仅表示单个动作。此外函数式接口定义了lambda表达式的目标类型。同时Object的公有方法也是函数式接口的隐式成员。
java中的 lambda表达式 格式:
()-> 123.45 // double f(){return 123.45}
()-> Math.random()*100 // double f(){ return Math.random()*100;}
(n)->(n%2)==0 // boolean f(Int n){ return (n%2)==0}
(n)->n*2 // 参数类型和返回类型各是什么呢?需要函数式接口的定义
可以显式指定n的类型: (int n) -> n * 2
public class Lambda { interface MyNumber{ double getValue(); default int getNum(){ return 10; } } public static void main(String[] args) { MyNumber mynum = ()-> 123; System.out.println(mynum.getValue()); System.out.println(mynum.getNum()); System.out.println(); final MyNumber mynum2 = ()-> Math.random() * 100 ; System.out.println(mynum2.getValue()); System.out.println(mynum2.getValue()); System.out.println(); MyNumber mynum3 = ()->{ int result =1; int n = (int)(mynum2.getValue()); System.out.println(n); for(int i = 1; i<n;i++){ result = (int)(Math.sqrt(result * i)) ; } return result; }; System.out.println(mynum3.getValue()); } }
public class Lambda { interface MyNumber<T extends Number>{ void getValue(T o); } public static void main(String[] args) { Lambda l = new Lambda(); l.forI().getValue(12); System.out.println(); l.print().getValue(0b1001); } <T extends Number> MyNumber<T> print(){ return (n)->{ System.out.println(n); }; } MyNumber<Integer> forI(){ return (n)->{ for (int i = 0; i < n; i++) { System.out.println(i); } }; } }
c#中的闭包:
namespace Lambda { class Program { static void Main() { Program aa = new Program(); List<Action> actions = aa.get(); //执行动作 foreach (Action action in actions) { // 是一个函数 action(); } Console.ReadKey(); } List<Action> get() { //定义动作组 List<Action> actions = new List<Action>(); for (int counter = 0; counter < 10; counter++) { actions.Add(() => { Console.WriteLine(counter); }); } return actions; } } }
c#中函数可以作为返回值,作为参数,可以柯里化,但函数不能嵌套函数
namespace Lambda { class Program { static void Main() { Program aa = new Program(); Console.WriteLine(aa.get2()(1)(2)); Console.WriteLine(aa.calculate(1, 2, aa.get2())); Console.WriteLine( aa.get3()( () => { Random r = new Random(); return r.Next(100); } )() ); Console.ReadKey(); } // 函数可以作为返回值,作为参数,可以柯里化 Func<int, Func<int, int>> get2() { return x => y => x + y; } int calculate(int a,int b,Func<int, Func<int, int>> f){ return f(a)(b); } Func<Func<int>, Func<bool>> get3() { return (x) => { return () => x() > 50; }; } } }
lambda表达式可以抛出异常
interface DoubleNumericArrayFunc{ double func(double[] n) throws EmptyArrayException; } class EmptyArrayException extends Exception{ EmptyArrayException(){ super("Array empty"); } } public class LambdaException { public static void main(String[] args) throws EmptyArrayException { double values[] = {1,2,3,4}; DoubleNumericArrayFunc average = (n)->{ double sum =0; if(n.length == 0){ throw new EmptyArrayException(); } for (int i = 0; i < n.length; i++) { sum += n[i]; } return sum / n.length; }; System.out.println("The avarage is : "+ average.func(values)); System.out.println("The avarage is : "+ average.func(new double[0])); } }
方法引用:
interface StringFunc{ String func(String n); } class MyStringOps{ static String strReverse(String str){ String result =""; int i; for (i = str.length() - 1; i >= 0; i--) { result += str.charAt(i); } return result; } } class LambdaRefMethod { static String stringOp(StringFunc sf,String s){ return sf.func(s); } public static void main(String[] args) { String inStr = "Lambda add power to java"; String outStr; /* * 可将 对 MyStringOps中声明的静态方法 strReverse 的引用 传递给 stringOp的第一个参数 * 因为strReverse 与 函数式接口 StringFunc 兼容(二者的函数类型一致),用strReverse提供 * 了StringFunc的方法实现。 */ outStr = stringOp(MyStringOps::strReverse,inStr); System.out.println("Orginal String : " + inStr); System.out.println("String Reversed : " + outStr); } }
上面是静态方法的引用传递,实例方法的引用传递,用实例对象引用方法
interface StringFunc{ String func(String n); } class MyStringOps{ String strReverse(String str){ String result =""; int i; for (i = str.length() - 1; i >= 0; i--) { result += str.charAt(i); } return result; } } class LambdaRefMethod { static String stringOp(StringFunc sf,String s){ return sf.func(s); } public static void main(String[] args) { String inStr = "Lambda add power to java"; String outStr; MyStringOps ops = new MyStringOps(); outStr = stringOp(ops::strReverse,inStr); System.out.println("Orginal String : " + inStr); System.out.println("String Reversed : " + outStr); } }
对于实例引用,也可以使用类名引用方法。 还可以 用super 引用 方法的 超类版本。
interface MyFunc<T>{ boolean func(T v1,T v2); } class HighTemp{ private int hTemp; HighTemp(int ht) { hTemp = ht; } boolean sameTemp(HighTemp ht2){ return hTemp == ht2.hTemp; } boolean lessThenTemp(HighTemp ht2){ return hTemp < ht2.hTemp; } } class LambdaRefMethod{ static <T> int counter(T[] vals,MyFunc<T> f,T v){ int count =0; for (int i = 0; i < vals.length; i++) { if(f.func(vals[i], v)){ count ++; } } return count; } public static void main(String[] args) { int count; HighTemp[] weekDayHighs ={ new HighTemp(89),new HighTemp(82), new HighTemp(90),new HighTemp(89), new HighTemp(89),new HighTemp(91), new HighTemp(84),new HighTemp(83) }; count = counter(weekDayHighs,HighTemp::sameTemp,new HighTemp(89)); System.out.println(count + " days had a high of 89"); HighTemp[] weekDayHighs2 ={ new HighTemp(32),new HighTemp(12), new HighTemp(24),new HighTemp(19), new HighTemp(18),new HighTemp(12), new HighTemp(-1),new HighTemp(13) }; count = counter(weekDayHighs2,HighTemp::sameTemp,new HighTemp(12)); System.out.println(count + " days had a high of 12"); count = counter(weekDayHighs,HighTemp::lessThenTemp,new HighTemp(89)); System.out.println(count + " days had a high less than 89"); count = counter(weekDayHighs2,HighTemp::lessThenTemp,new HighTemp(19)); System.out.println(count + " days had a high less than 19"); } }
泛型方法的引用
interface MyFunc<T>{ int func(T[] vals,T v); } class MyArrayOps{ static <T> int countMatching(T[] vals,T v){ int count =0; for (int i = 0; i < vals.length; i++) { if(vals[i] == v)count++; } return count; } } class LambdaRefMethod{ static <T> int myOp(MyFunc<T> f, T[] vals,T v){ return f.func(vals, v); } public static void main(String[] args) { Integer[] vals = { 1,2,3,4,2,3,4,4,5 }; String[] strs ={ "one","two","three","two" }; int count; count = myOp(MyArrayOps::<Integer>countMatching,vals,4); System.out.println("vals contains " + count + " 4s"); count = myOp(MyArrayOps::<String>countMatching,strs,"two"); System.out.println("vals contains " + count + " 4s"); } }
import java.util.*; class MyClass{ private int val; MyClass(int v){ val = v;} int getVal(){ return val;} } class LambdaRefMethod{ static int compareMC(MyClass a,MyClass b){ return a.getVal() - b.getVal(); } public static void main(String[] args) { ArrayList<MyClass> al = new ArrayList<>(); al.add(new MyClass(1)); al.add(new MyClass(4)); al.add(new MyClass(2)); al.add(new MyClass(9)); al.add(new MyClass(3)); al.add(new MyClass(7)); // MyClass 既没有定义自己的比较方法,也没有实现Comparator接口,通过方法引用 // 简化了代码 MyClass maxValObj = Collections.max(al,LambdaRefMethod::compareMC); System.out.println("Maximum value is : "+maxValObj.getVal() ); } }
构造函数的引用
interface MyFunc<R,T>{ R func(T n); } class MyClass<T>{ private T val; MyClass(T v){ val = v;} MyClass(){val = null;} T getVal(){return val;} } class MyClass2{ String str; MyClass2(String s){str =s;} MyClass2(){str ="";} String getVal(){return str;} } interface MyArrayCreator<T>{ T func (int n); } public class LambdaConstRef { static <R,T> R myClassFactory(MyFunc<R,T> cons,T v){ return cons.func(v); } public static void main(String[] args) { MyFunc<MyClass<Double>,Double> myClassCons = MyClass<Double>::new; MyClass<Double> mc = myClassFactory(myClassCons, 100.1); System.out.println("val in mc is " + mc.getVal()); MyFunc<MyClass2,String> myClassCons2 = MyClass2::new; MyClass2 mc2 = myClassFactory(myClassCons2, "lambda"); System.out.println("str in mc2 is " + mc2.getVal()); // 数组 MyArrayCreator<MyClass2[]> mcArrayCons = MyClass2[]::new; MyClass2[] aa = mcArrayCons.func(2); aa[0] = new MyClass2("22"); aa[1] = new MyClass2("122"); System.out.println("aa[1] : " +aa[1].getVal()); MyArrayCreator<MyClass<?>[]> mcArrayCons2 = MyClass<?>[]::new; MyClass<?>[] bb = mcArrayCons2.func(2); bb[0] = new MyClass<String>("22"); bb[1] = new MyClass<String>("122"); System.out.println("bb[1] : " +bb[1].getVal()); } }
java.util.function.* 中提供了 一些预定义的函数式接口。
UnaryOperator<T> : 对类型为T的对象应用一元运算,并返回类型为T的结果。包含的方法为apply()
BinaryOperator<T> :对类型为T的两个对象应用操作,并返回类型为T的结果,包含的方法为apply()
Consumer<T> :对类型为T的对象应用操作,包含的方法为accept()
Supplier<T> :返回类型为T的对象,包含的方法为get()
Function<T,R>:对类型为T的对象应用操作,返回类型为R的结果,包含的方法为apply()
Predicate<T> :确定类型为T的对象是否满足某种约束,并返回指出结果的布尔值,包含的方法为test()
import java.util.function.Function; public class LambdaFunction { public static void main(String[] args) { Function<Integer,Integer> factorial = (n)->{ int result = 1; for(int i =1;i<= n;i++){ result =i * result; } return result; }; System.out.println("the factorial of 3 : "+factorial.apply(3)); System.out.println("the factorial of 5 : "+factorial.apply(5)); } }
import java.util.stream.* 在更高的抽象层次上对集合进行操作。
惰性求值,返回加工后的流。
及早求值返回另一个值。
import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; class FunctionalProgramming { public static void main(String[] args) { // map List<String> s = Stream.of('a','b','c').map(c->(char)(c+4)+"").collect(Collectors.toList()); // filter List<String> s2 = Stream.of("a","b","c","d").filter(str->str!="d").collect(Collectors.toList()); // flatMap List<String> s3 = Stream.of(s,s2).flatMap(numbers->numbers.stream()).collect(Collectors.toList()); System.out.println(s3); // reduce String all = s3.stream().reduce("all: ",(o,str)->o+str); System.out.println(all); // min String min = s3.stream().min(Comparator.comparing(str->str.charAt(0))).get(); System.out.println(min); // max String max = s3.stream().max(Comparator.comparing(str->str.charAt(0))).get(); System.out.println(max); } }
flatMap的实现与swift中差别很大
总结:
缺点
1。两个不方便
2。没有语言层面的惰性求值
优点
1。lambda的加入 简化了代码,易读的代码可以更多地表达业务逻辑的意图,而不是它的实现机制。也更易于维护,更可靠,更不容易出错。
2。面向对象是对数据抽象,函数式编程则抽象了行为。让代码在多核CPU上高效运行。
其他主题 :
类型修饰符: transient, volatile
class T{
transient int a;
int b;
}
如果将T的对象写入永久存储区域时,不会保存a的内容,但会保存b的内容。
volatile 告诉编译器,必须总是精确读取变量的最新值。
instanceof:
strictfp: 在java2 时,浮点计算模型扫尾宽松了一些, 这个修饰符告诉java定义内的所有浮点数都采用原始的计算模型。
native: 声明本地代码方法。
偶尔可能需要调用非JAVA语言编写的子例程,这类子例程作为可执行代码(对于正在使用的CPU和环境而言是可执行代码,即本地代码)而存在。一旦方法使用native声明,就可以从JAVA程序内部调用这些方法。
public class NativeDemo { int i; public static void main(String args[]){ NativeDemo ob = new NativeDemo(); ob.i = 10; System.out.println("This is ob.i before the native method :" + ob.i); ob.test(); System.out.println("This is ob.i after the native method :"+ ob.i); } // declare native method public native void test(); // load DLL that contains static method static{ // 动态链接库,从JDK 8开始可以创建静态链接库 System.loadLibrary("NativeDemo"); } }
assert:
public class AssertDemo { static int val = 3; static int getNum(){ return val--; } public static void main(String args[]){ int n = 0; for(int i = 0; i<10;i++){ n = getNum(); assert n > 0 :"n is negative"; System.out.println("n is "+ n); } } }
Run -> Run Configurations -> Arguments页签 -> VM arguments文本框中加上断言开启的标志:-enableassertions 或者-ea 就可以了
静态导入: 导入接口或类的静态成员
import static java.lang.Math.sqrt; import static java.lang.Math.pow; //import static java.lang.Math.*; public class StaticImport { public static void main(String[] args) { double side1,side2; double hypot; side1 = 3.0; side2 = 4.0; hypot = sqrt(pow(side1,2) +pow(side2,2)); System.out.println("Given sides of lengths " + side1 + " and " + side2 + " the hypotenuse is " + hypot); } }
优点:简化并缩短了使用静态成员的代码
缺点: 1,可读性变差(该静态成员来自哪里)
2,可能的命名空间冲突
仅针对重复使用某静态成员时设计,如执行一系列数学计算。但不能滥用这一特性。
通过this()调用重载的构造函数
class ThisConstructor { int a; int b; ThisConstructor(int i,int j){ a = i; b = j; } ThisConstructor(int i){ a = i; b = i; } ThisConstructor(){ a = 0; b = 0; } } class ThisConstructor2 { int a; int b; ThisConstructor2(int i,int j){ a = i; b = j; } ThisConstructor2(int i){ this(i,i); } ThisConstructor2(){ this(0); } }
this() 节省了代码,却会增加构造对象的开销
适用于 包含大量初始化代码的构造函数,适用于创建少量对象的类,却不适合于那些只简单设置少量变量值的构造函数,不适合要大量创建对象的类。
因super() 和 this() 都必须是构造函数的第一条语句,所有二者不能同时使用。
紧凑API配置文件
JDK 8 新增功能,
内存管理 :
swift中的引用计数管理内存,会造成环形引用无法回收内存。用JAVA测试了一下,正常回收了
class Employee{ Manager m; Employee(Manager m){ this.m = m; System.out.println("init Employee"); } protected void finalize(){ System.out.println("deinit Employee"); } } class Manager{ Employee e = null; Manager(){ System.out.println("init Manager"); } void setEmployee(Employee e){ this.e =e; } protected void finalize(){ System.out.println("deinit Manager"); } } public class GCTest { static void f(){ Manager m = new Manager(); Employee e = new Employee(m); m.setEmployee(e); } public static void main(String[] args) { f(); System.gc(); } }