热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JDK源码简析java.lang包中的基础类库

在本节中,简析java.lang包所包含的基础类库,比如:Object、System、Runtime、Class、基本数据类型、异常和错误、注解类等,当我们新写一个class时,这


题记


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,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();
	}
}


以上代码将顺序输出:A:2、B:2、C:2、A:1、B:1、C:1 。


Class和反射类


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


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中定义了很多,用来描述我们的代码可能遇到的各种类型的错误,这里不一一整理描述。


Runtime


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包中的基础类库


推荐阅读
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
author-avatar
落墨
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有