◎筱米加步枪◎.Blog

Happy coding

Java native方法小谈

看jdk中的Object对象的源码,发现一些方法比如public native int hashCode();方法是没有实现体的,只是前面加了native修饰符,他表示这个方法的实现是与平台相关的其他API实现,也就是说并不是java语言来实现的,比如是由C/C++来实现。这就是Java与外界环境(比如操作系统)交换信息的渠道。

本来想找找这段具体实现的代码在哪里,很遗憾找不到的说,网上也说是看不到的这些代码的。

生成javadoc时生成包说明的方法

利用Eclipse生成的javadoc是没有输出包说明的,如何才能使javadoc输出包说明呢?方法如下:

在每个包下建立一个package.html,该html中<body>中所指定的内容就是javadoc输出时包名的描述

例如:在html中指定如下:

<body>
缓存工具集,提供平台公共的缓存工具.
</body>

则显示效果如下:

关于Arrays.asList的问题

将数组转成List问题,通常我们习惯这样写成:List<String> list = Arrays.asList("1","2");

于是我们这样就得到了一个list,但是这个List的实现类是java.util.Arrays.ArrayList这个类(而不是java.util.ArrayList)。

剖析JDK源代码可以发现,java.util.Arrays.ArrayList(就是转换出来list)它是继承了java.util.AbstractList这个类。

再来看看java.util.AbstractList类是啥样子的?可以发现

public E set(int index, E element)

public E set(int index, E element)

public E remove(int index)

public void add(int index, E element)  | public boolean add(E e)调用add(int index, E element)

以上方法的实现全部是抛出UnsupportedOperationException异常。

因此有Arrays.asList转换出来的List他其实是一个AbstractList,他可以像List一样访问,但是不可其做任何修改操作。

这也说明了,为什么Arrays.asList出来的List,对其做add、remove操作为抛出UnsupportedOperationException异常,从JDK代码角度上,原因在此。

换句话说,其实java.util.Arrays.ArrayList其实只是对数组做了一个装饰,可以看到里面的实现,E get(int index)、E set(int index, E element)等方法都是对数组的操作,他的目的只是提供了可以像访问List那样来访问数组而已。本质上其实还是一个数组。

关于字符串非空判断效率问题

做一个字符串非空的判断,我们经常如下这样写:

		if(str == null || "".equals(str)){
			//具体操作
		}

这是一种很正常的写法,但是如果去比较字符串的长度是否为0的话,效率是更高的。贴个JDK的equals方法的源代码:

    public boolean equals(Object anObject) {
	if (this == anObject) {
	    return true;
	}
	if (anObject instanceof String) {
	    String anotherString = (String)anObject;
	    int n = count;
	    if (n == anotherString.count) {
		char v1[] = value;
		char v2[] = anotherString.value;
		int i = offset;
		int j = anotherString.offset;
		while (n-- != 0) {
		    if (v1[i++] != v2[j++])
			return false;
		}
		return true;
	    }
	}
	return false;
    }

再来看看str.length()的方法,则是直接返回其大小。两个对比,很明显的length()方法要优化很多。

所以做字符串非空判断尽量写成如下方式:

		if(str == null || str.length() == 0){
			//具体操作
		}

其实也可以用apache-common-lang包下StringUtils.isEmpty(String src);方法判断即可,里面的实现就是用长度来判断的。

经理透露,很多笔试面试题经常都会考这样的问题,如果你用equals来判断,那么在面试官的印象就要大大折扣啦。

Java主线程等待子线程执行完毕-CountDownLatch

想做的一个程序如题,主要是想统计子线程都执行完毕所用的时间,网上搜索到了CountDownLatch这个类,这个工具类可以理解为计数器。在这里用于表示正在运行的线程数,当一个子线程结束的时候,将这个计数器减一,最后在主线程的一个地方等待子线程全部执行完毕,再继续运行等待后面的程序。写了个Demo程序,如下:

//子线程
public class SubThread extends Thread{

	//子线程记数器,记载着运行的线程数
	private CountDownLatch runningThreadNum;

	public SubThread(CountDownLatch runningThreadNum){
		this.runningThreadNum = runningThreadNum;
	}
	
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+"-start");
		System.out.println(Thread.currentThread().getName()+"-do something");
		System.out.println(Thread.currentThread().getName()+"-end");
		runningThreadNum.countDown();//正在运行的线程数减一
	}
}
//Main主线程
public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		long startTime = System.currentTimeMillis();
		int threadNum = 5; //线程数
		//定义正在运行的线程数
		CountDownLatch runningThreadNum = new CountDownLatch(threadNum);
		System.out.println(Thread.currentThread().getName()+"-start");
		//创建多个子线程
		for (int i = 0; i < threadNum; i++) {
			new SubThread(runningThreadNum).start();
		}
		//等待子线程都执行完了再执行主线程剩下的动作
		runningThreadNum.await();
		System.out.println(Thread.currentThread().getName()+"-end");
		long endTime = System.currentTimeMillis();
		System.out.println("runningTime:"+(endTime-startTime));
	}
}
main-start
Thread-0-start
Thread-0-do something
Thread-0-end
Thread-1-start
Thread-1-do something
Thread-1-end
Thread-2-start
Thread-2-do something
Thread-2-end
Thread-4-start
Thread-3-start
Thread-4-do something
Thread-3-do something
Thread-4-end
Thread-3-end
main-end
runningTime:16

看到打印的结果,满足我现在的要求。笔记下~~

Java深克隆(序列化方式)

对于Java的克隆技术,标准的方式是:首先实现Cloneable接口,然后重写clone方法,调用父类clone进行克隆。

介绍另外一种方法,该方法的原理:

利用流将序列化的对象写在流里,因为写在流里面的对象就是原对象的一份拷贝,而原对象还在Java虚拟机里(JVM)里,再从流里面读取出来得到的对象就是得到一份克隆对象。

注意:需要克隆的对象必须实现Serializable接口

接着之前的场景,看下面的例子:

public class User implements Serializable{
	
	private static final long serialVersionUID = 1L;

	private String userName;

	private Address address;
	
	public User(String userName) {
		super();
		this.userName = userName;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	public Object copy() throws IOException, ClassNotFoundException{
		//将对象序列化后写在流里,因为写在流里面的对象是一份拷贝,
		//原对象仍然在JVM里
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		oos.writeObject(this);
		
		ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
				bos.toByteArray()));
		return ois.readObject();
	}
}
public class Address implements Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private String addressName;
	
	public Address(String addressName) {
		super();
		this.addressName = addressName;
	}

	public String getAddressName() {
		return addressName;
	}

	public void setAddressName(String addressName) {
		this.addressName = addressName;
	}
}
/**
 * 测试克隆-深克隆(使用序列化的方式进行深克隆)
 * 深克隆不但克隆当前的对象,而且还克隆该对象所引用的对象
 * @author CST
 */
public class TestClone {

	public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
		User chen = new User("CST");
		chen.setAddress(new Address("福州"));
		
		User liu = (User) chen.copy();
		liu.setUserName("LXF");
		liu.getAddress().setAddressName("泉州");
		System.out.println(chen.getUserName()+"="+chen.getAddress().getAddressName());
	}
}

输出结果:CST=福州
效果同实现Cloneable接口方式一样

Java克隆技术

前些天,根据需要,加入了克隆技术,学习了下,总结如下:

Java的对象都是引用,当将一个对象赋值给另外一个对象的时候,也就是说指针(当然,java没有指针的概念)同指向同一块内存地址,这个时如果对一个对象进行修改,也必然会修改另外一个对象的值,这明显不是我们想要的,解决这个问题,可以引入克隆技术,我们可以克隆一个对象出来,使得对克隆出来的对象修改不会改变原始对象的值。

克隆分为:浅克隆和深克隆。

浅克隆是指:浅克隆只是克隆当前的对象,不克隆该对象所应用的对象

深克隆是指:深克隆不但克隆当前的对象,而且还克隆该对象所引用的对象

看具体实例:

(模拟场景:用户对象、地址对象、用户对象拥有地址对象)

一、浅克隆

//克隆必须实现Cloneable,调用父类的clone()方法
public class User implements Cloneable{
	
	private String userName;

	private Address address;
	
	public User(String userName) {
		super();
		this.userName = userName;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	/*
	 * 克隆当前对象,得到克隆后的对象
	 * @see java.lang.Object#clone()
	 */
	@Override
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}
//地址对象
public class Address{ 
	
	private String addressName;
	
	public Address(String addressName) {
		super();
		this.addressName = addressName;
	}

	public String getAddressName() {
		return addressName;
	}

	public void setAddressName(String addressName) {
		this.addressName = addressName;
	}
}
/**
 * 测试克隆-浅克隆
 * 浅克隆只是克隆当前的对象,不克隆该对象所应用的对象
 * @author CST
 */
public class TestClone {

	public static void main(String[] args) throws CloneNotSupportedException {
		User chen = new User("CST");
		chen.setAddress(new Address("福州"));
		
		User liu = (User) chen.clone();
		liu.setUserName("LXF");
		liu.getAddress().setAddressName("泉州");
		System.out.println(chen.getUserName()+"="+chen.getAddress().getAddressName());
	}
}

输出结果:CST=泉州

可以看到,虽然User对象可以单独操作,但是视图去修改User下的Address值,仍然是改变了原始用户下的Address值

二、深克隆:

public class User implements Cloneable{
	
	private String userName;

	private Address address;
	
	public User(String userName) {
		super();
		this.userName = userName;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	//克隆
	@Override
	public Object clone() throws CloneNotSupportedException {
		//克隆对象的同时,也克隆user下的应用对象Address
		User user = (User) super.clone();
		Address address = (Address) user.getAddress().clone();
		user.setAddress(address);
		return user;
	}
}
public class Address implements Cloneable{
	
	private String addressName;
	
	public Address(String addressName) {
		super();
		this.addressName = addressName;
	}

	public String getAddressName() {
		return addressName;
	}

	public void setAddressName(String addressName) {
		this.addressName = addressName;
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

测试类同上,

输出结果:CST=长乐

这样,才是我们想要的结果。无论修改当前对象还是当前对象所包含的对象,都是两个独立的操作,互不影响。

volatile关键字

JDBC实现批处理SQL语句最后不能加分号