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=长乐
这样,才是我们想要的结果。无论修改当前对象还是当前对象所包含的对象,都是两个独立的操作,互不影响。