JDK,Java Development Kit。
我们必须先认识到,JDK只是,仅仅是一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含量来说,还是在一个层级上,它们都是需要被编译成字节码,在JRE中运行的,JDK编译后的结果就是jre/lib下得rt.jar,我们学习使用它的目的是加深对Java的理解,提高我们的Java编码水平。
本系列所有文章基于的JDK版本都是1.7.16。
在本节中,简析java.lang包所包含的基础类库,当我们新写一个class时,这个package里面的class都是被默认导入的,所以我们不用写import java.lang.Integer这样的代码,我们依然使用Integer这个类,当然,如果你显示写了import java.lang.Integer也没有问题,不过,何必多此一举呢
默认所有的类都继承自Object,Object没有Property,只有Method,其方法大都是native方法(也就是用其他更高效语言,一般是c实现好了的),
Object没有实现clone(),实现了hashCode(),哈希就是对象实例化后在堆内存的地址,用“==”比较两个对象,实际就是比较的内存地址是否是一个,也就是hashCode()是否相等,
默认情况下,对象的equals()方法和==符号是一样的,都是比较内存地址,但是有些对象重写了equals()方法,比如String,使其达到比较内容是否相同的效果
另外两个方法wait()和notify()是和多线程编程相关的,多线程里面synchronized实际就是加锁,默认是用this当锁,当然也可以用任何对象当锁,wait()上锁,线程阻塞,notify()开锁,收到这个通知的线程运行。以下代码示例:
class Test implements Runnable { private String name; private Object prev; private Object self; public Test(String name,Object prev,Object self) { this.name=name; this.prev = prev; this.self = self; } @Override public void run() { int count = 2; while(count>0) { synchronized(prev) { synchronized(self) { System.out.println(name+":"+count); count--; self.notify(); //self解锁 } try { prev.wait(); //prev上锁 } catch(InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws Exception { Object a = new Object(); Object b = new Object(); Object c = new Object(); Test ta = new Test("A",c,a); Test tb = new Test("B",a,b); Test tc = new Test("C",b,c); new Thread(ta).start(); Thread.sleep(10); new Thread(tb).start(); Thread.sleep(10); new Thread(tc).start(); } }
Java程序在运行时每个类都会对应一个Class对象,可以从Class对象中得到与类相关的信息,Class对象存储在方法区(又名Non-Heap,永久代),当我们运行Java程序时,如果加载的jar包非常多,大于指定的永久代内存大小时,则会报出PermGen错误,就是Class对象的总计大小,超过永久代内存的缘故。
Class类非常有用,在我们做类型转换时经常用到,比如以前用Thrift框架时,经常需要在Model类型的对象:Thrift对象和Java对象之间进行转换,需要手工书写大量模式化代码,于是,就写了个对象转换的工具,在Thrift对象和Java对象的Property名字一致的情况下,可以使用这个工具直接转换,其中大量使用了Class里面的方法和java.lang.reflect包的内容。
关于Calss类,方法众多,不详述。下面附上这个Thrift工具的代码。
import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Thrift前缀的对象和不含Thrift前缀的对象相互转换. * 参考: * http://blog.csdn.net/it___ladeng/article/details/7026524 * http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html * http://www.cnblogs.com/bingoidea/archive/2009/06/21/1507889.html * http://java.ccidnet.com/art/3539/20070924/1222147_1.html * http://blog.csdn.net/justinavril/article/details/2873664 */ public class ThriftUtil { public static final Integer THRIFT_PORT = 9177; /** * Thrift生成的类的实例和项目原来的类的实例相关转换并赋值 * 1.类的属性的名字必须完全相同 * 2.当前支持的类型仅包括:byte,short,int,long,double,String,Date,List * 3.如果有Specified列,则此列为true才赋值,否则,不为NULL就赋值 * @param sourceObject * @param targetClass * @param toThrift:true代表把JavaObject转换成ThriftObject,false代表把ThriftObject转换成JavaObject,ThriftObject中含有Specified列 * @return */ public static Object convert(Object sourceObject,Class> targetClass,Boolean toThrift) { if(sourceObject==null) { return null; } //对于简单类型,不进行转换,直接返回 if(sourceObject.getClass().getName().startsWith("java.lang")) { return sourceObject; } Class> sourceClass = sourceObject.getClass(); Field[] sourceFields = sourceClass.getDeclaredFields(); Object targetObject = null; try { targetObject = targetClass.newInstance(); } catch (InstantiationException e1) { e1.printStackTrace(); } catch (IllegalAccessException e1) { e1.printStackTrace(); }; if(targetObject==null) { return null; } for(Field sourceField:sourceFields) { try { //转换时过滤掉Thrift框架自动生成的对象 if(sourceField.getType().getName().startsWith("org.apache.thrift") ||sourceField.getName().substring(0,2).equals("__") ||("schemes,metaDataMap,serialVersionUID".indexOf(sourceField.getName())!=-1) ||(sourceField.getName().indexOf("_Fields")!=-1) ||(sourceField.getName().indexOf("Specified")!=-1) ) { continue; } //处理以DotNet敏感字符命名的属性,比如operator String sourceFieldName = sourceField.getName(); if(sourceFieldName.equals("operator")) { sourceFieldName = "_operator"; } else { if(sourceFieldName.equals("_operator")) { sourceFieldName = "operator"; } } //找出目标对象中同名的属性 Field targetField = targetClass.getDeclaredField(sourceFieldName); sourceField.setAccessible(true); targetField.setAccessible(true); String sourceFieldSimpleName = sourceField.getType().getSimpleName().toLowerCase().replace("integer", "int"); String targetFieldSimpleName = targetField.getType().getSimpleName().toLowerCase().replace("integer", "int"); //如果两个对象同名的属性的类型完全一致:Boolean,String,以及5种数字类型:byte,short,int,long,double,以及List if(targetFieldSimpleName.equals(sourceFieldSimpleName)) { //对于简单类型,直接赋值 if("boolean,string,byte,short,int,long,double".indexOf(sourceFieldSimpleName)!=-1) { Object o = sourceField.get(sourceObject); if(o != null) { targetField.set(targetObject, o); //处理Specified列,或者根据Specified列对数值对象赋NULL值 try { if(toThrift) { Field targetSpecifiedField = targetClass.getDeclaredField(sourceFieldName+"Specified"); if(targetSpecifiedField != null) { targetSpecifiedField.setAccessible(true); targetSpecifiedField.set(targetObject, true); } } else { Field sourceSpecifiedField = sourceClass.getDeclaredField(sourceFieldName+"Specified"); if(sourceSpecifiedField != null && "B,S,B,I,L,D".indexOf(targetField.getType().getSimpleName().substring(0,1))!=-1 ) { sourceSpecifiedField.setAccessible(true); if(sourceSpecifiedField.getBoolean(sourceObject)==false) { targetField.set(targetObject, null); } } } } catch (NoSuchFieldException e) { //吃掉NoSuchFieldException,达到效果:如果Specified列不存在,则所有的列都赋值 } } continue; } //对于List if(sourceFieldSimpleName.equals("list")) { @SuppressWarnings("unchecked") List sourceSubObjs = (ArrayList)sourceField.get(sourceObject); @SuppressWarnings("unchecked") List targetSubObjs = (ArrayList)targetField.get(targetObject); //关键的地方,如果是List类型,得到其Generic的类型 Type targetType = targetField.getGenericType(); //如果是泛型参数的类型 if(targetType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) targetType; //得到泛型里的class类型对象。 Class> c = (Class>)pt.getActualTypeArguments()[0]; if(sourceSubObjs!=null) { if(targetSubObjs==null) { targetSubObjs = new ArrayList(); } for(Object obj:sourceSubObjs) { targetSubObjs.add(convert(obj,c,toThrift)); } targetField.set(targetObject, targetSubObjs); } } continue; } } //转换成Thrift自动生成的类:Thrift没有日期类型,我们统一要求日期格式化成yyyy-MM-dd HH:mm:ss形式 if(toThrift) { if(sourceFieldSimpleName.equals("date")&&targetFieldSimpleName.equals("string")) { Date d = (Date)sourceField.get(sourceObject); if(d!=null) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); targetField.set(targetObject,sdf.format(d)); } continue; } } else { if(sourceFieldSimpleName.equals("string")&&targetFieldSimpleName.equals("date")) { String s = (String)sourceField.get(sourceObject); if(s!=null) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); targetField.set(targetObject,sdf.parse(s)); } continue; } } //对于其他自定义对象 targetField.set(targetObject, convert(sourceField.get(sourceObject),targetField.getType(),toThrift)); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ParseException e) { e.printStackTrace(); } } return targetObject; } }
类类型 | 原生类型(primitive) | 代表意义 |
Boolean | boolean | 布尔 |
Character | char | 单个字符,例如‘a‘,‘中‘ |
Byte | byte | 8位有符号整型 |
Short | short | 16位有符号整型 |
Integer | int | 32位有符号整型 |
Long | long | 64位有符号整型 |
Float | float | 单精度浮点 |
Double | double | 双精度浮点 |
了解到数据类型,就要了解一点编码(数字在计算机中的01编码)的知识,计算机是用01表示所有类型的,第一位是符号位,0+1-,java中得整型都是有符号的。
对于数字,定义了原码、反码和补码。
正数的原码、反码和补码都是一样的,负数的反码是除符号位之外,所有位取反,补码是反码+1,以Byte类型为例:
能表示的数字范围是-128~127,总计256个数,对于0~127,表示为:0000000~01111111,即0~2^7-1,对于负数,-128~-1,表示如下:
-127~-1,其原码分别为11111111~10000001,换算成补码是10000001~11111111,然后10000000代表-128,-128只有补码,没有原码和反码。
补码的设计目的:
使符号位能与有效值部分一起参加运算,从而简化运算规则。补码机器数中的符号位,并不是强加上去的,是数据本身的自然组成部分,可以正常地参与运算。
使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计。
反码是原码与补码转换的一个中间过渡,使用较少。
所有这些转换都是在计算机的最底层进行的,而在我们使用的汇编、c等其他高级语言中使用的都是原码。
除此之外,JDK原码还经常使用>>、>>>运算符,>>是有符号移位,高位补符号位,右移;>>>是无符号移为,高位补0,右移。
关于数据类型的内存存储,如下描述一下:
原生类型,比如int i = 3,不论是变量,还是值,都是存储在栈中的;类类型,比如Integer i = 3,则变量是存储在栈中,对象是存储在堆中的一个对象,既然Integer是这样,那么Integer a = 3,Integer b = 3,请问 a == b,是否成立?
答案是成立的,这是因为JVM对基本类型做了优化,当对于b赋值时,会看是否存在值为b的Integer对象,因为已经存在了,所以直接进行了b = a赋值操作,也就是a和b都指向同一片内存地址了,所以a == b成立。
那么Integer a = new Integer(3),Integer b = new Integer(3),a == b是否成立?现在因为我们new了,强制新分配了内存,所以a和b不再指向同一片内存地址,所以 a == b 不成立,但是 a.equals(b) 是成立的,因为a和b所代表的Integer对象的primitive值是相同的。
String a = "abc",String b = "abc",如上对于Integer的讲解一样,a == b是成立的,原理也是一样的。
对于String s = a + b这样的操作,每次操作,都会在堆中开辟一块内存空间,所以对于频繁大量的字符操作,性能低,所以对于大量字符操作,推荐使用的StringBuffer和StringBuilder,其中StringBuffer是线程安全的,StringBuilder是1.5版本新加的,是非线程安全的。
Exception和Error都继承自Throwable对象,Exception分为两类,RuntimeException(运行时异常,unchecked)和一般异常(checked),Error是比较严重的错误,也是unchecked。
简单地讲,checked是必须用try/catch 或者throw处理的,可以在执行过程中恢复的,比如java.io.FileNotFoundException;而unchecked异常则是不需要try/catch处理的,比如java.lang.NullPointerException。
在比较流行的语言中,Java是唯一支持checked异常,要求我们必须进行处理的语言,这有利有弊。
关于异常类、错误类,在JDK中定义了很多,用来描述我们的代码可能遇到的各种类型的错误,这里不一一整理描述。
java.lang包里有很多运行时环境相关的类,可以查看运行时环境的各种信息,比如内存、锁、安全、垃圾回收等等。见过如下钩子代码,在JVM关闭时,执行一些不好在程序计算过程中进行的资源释放工作,如下:
public class MongoHook { static void addCloseHook(){ Runtime.getRuntime().addShutdownHook( new Thread(){ @Override public void run() { MongoDBConn.destoryAllForHook() ; } }) ; } }
Thread、Runnable、ThreadLocal等,关于多线程的更多知识,可以参阅。
Clonable:生命式接口
Comparable:对象比较,泛型类
Appendable:
CharSequence:统一的字符串只读接口,共有4个方法,length()、charAt()、subSequence()、toString()。
Readable:
Iterable:迭代器
主要在java.lang.annotation包中,注解类用@interface来进行定义,注解类的作用范围可以是方法、属性、包等,作用失效可以是Source、Runtime、Class。
比如Override就是一个注解类,用来标记实现接口中定义的类,其源码如下;
package java.lang; import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
JDK源码简析--java.lang包中的基础类库