在java6中有一个新类Console,能够实现无回显输入,用于安全输入!以下是代码样例:
import java.io.*; import java.util.*; public class ConsoleDemo { public static void main(String[] args) { Console console = System.console(); if (console == null) { System.err.println("Failed to get console!"); return; } String name = console.readLine("Please type your name:"); char[] password1 = null; char[] password2 = null; console.printf("Your name is %s\n", name); password1 = console.readPassword("Please type your password:"); password2 = console.readPassword("Please type your password again:"); if (!Arrays.equals(password1, password2)) { System.err.println("Password doesn't match the confirmation."); return; } console.printf("Hi, %s, Your password is '%s'\n", name, new String(password1)); Arrays.fill(password1, '\0'); /* 安全清理 */ Arrays.fill(password2, '\0'); } }
这个问题很简单,就是在一个无序数组中判断某个元素是否存在,当然如果自己定义数据结构,可以使用trie,hash,如果不要求完全准确,允许误判,还能使用布隆!显然不能使用二分搜索,因为数组是无序的!当我们要经常搜索时,排序是必要的,或者使用其他数据结构。
这篇文章讨论了下http://www.programcreek.com/2014/04/check-if-array-contains-a-value-java/#more-12168,于是自己测试了下,下面是我的代码(必须使用java8版本以上):
import java.util.*; interface StringPolicy { boolean contains(String[] arr, String value); } interface LongPolicy { boolean contains(long[] arr, long value); } class Policies { public static boolean useList(String[] arr, String value) { return Arrays.asList(arr).contains(value); } public static boolean useList(long[] arr, long value) { return Arrays.asList(arr).contains(value); } public static boolean useSet(String[] arr, String value) { return new HashSet<>(Arrays.asList(arr)).contains(value); } public static boolean useSet(long[] arr, long value) { return new HashSet<>(Arrays.asList(arr)).contains(value); } public static boolean useLoop(String[] arr, String value) { for (String s : arr) { if (s.equals(value)) return true; } return false; } public static boolean useLoop(long[] arr, long value) { for (long l : arr) { if (l == value) return true; } return false; } public static boolean useStream(String[] arr, String value) { return Arrays.stream(arr).anyMatch(value::equals); } public static boolean useStream(long[] arr, long value) { return Arrays.stream(arr).anyMatch(l -> l == value); } public static boolean useParallelStream(String[] arr, String value) { return Arrays.stream(arr).parallel().anyMatch(value::equals); } public static boolean useParallelStream(long[] arr, long value) { return Arrays.stream(arr).parallel().anyMatch(l -> l == value); } } public class SearchDemo { public static void fill(String[] arr) { for(int i = 0; i < arr.length; ++i) { arr[i] = String.valueOf(i + 1); } } public static void fill(long[] arr) { for (int i = 0; i < arr.length; ++i) { arr[i] = i + 1; } } public static void fill(String[] arr, String value) { Arrays.fill(arr, value); } public static void fill(long[] arr, long value) { Arrays.fill(arr, value); } public static void test(String name, StringPolicy p, String[] arr, String value, int times) { long start = System.nanoTime(); for (int i = 0; i < times; ++i) { p.contains(arr, value); } long end = System.nanoTime(); long d = (end - start) / 1_000_000; System.out.printf("%s: %d\n", name, d); } public static void test(String name, LongPolicy p, long[] arr, long value, int times) { long start = System.nanoTime(); for (int i = 0; i < times; ++i) { p.contains(arr, value); } long end = System.nanoTime(); long d = (end - start) / 1_000_000; System.out.printf("%s: %d\n", name, d); } public static void main(String[] args) { String[] arr1 = new String[10_000]; long[] arr2 = new long[10_000]; fill(arr1); fill(arr2); System.out.println("******************* String **************************"); test("useLoop", Policies::useLoop, arr1, "A", 10_000); test("useList", Policies::useList, arr1, "A", 10_000); test("useSet", Policies::useSet, arr1, "A", 10_000); test("useStream", Policies::useStream, arr1, "A", 10_000); test("useParallelStream", Policies::useParallelStream, arr1, "A", 10_000); System.out.println("******************* Long **************************"); test("useLoop", Policies::useLoop, arr2, -1, 10_000); test("useList", Policies::useList, arr2, -1, 10_000); test("useSet", Policies::useSet, arr2, -1, 10_000); test("useStream", Policies::useStream, arr2, -1, 10_000); test("useParallelStream", Policies::useParallelStream, arr2, -1, 10_000); }
结果输出是:
众所周知,一般使用java实现单例模式有两种方法,分别为急切式(饥饿式)和双重加锁式,急切式就是在声明时即创建,这样在类加载时就已经创建好了,即时我们可能并不不需要它,它的生命周期是永久的,造成内存泄漏的可能!第二种方式是lazy的,只有在使用时创建,实现了延迟加载。代码为
1.急切式
class Singleton { private final static Singleton instance = new Singleton(); private Singleton(){ } public static Singleton getInstance() { return instance; } }
2. 双重加锁式
class Singleton { private volatile static Singleton instance = null; private Singleton(){ } public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
还有一种方法是使用内部类的方法,这主要为克服方法1中的问题,既能实现延迟加载,又能保证线程安全,而且性能不错。代码为:
class Singleton { private Singleton() { } private static class Holder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }
以上方法都是通过使用private构造方法来阻止外部直接创建对象,但如果使用反射机制,则不能保证实例的唯一性了!!!
public class SingletonDemo { //@SuppressWarnings("unchecked") //public static <T> T newInstance(Class<?> cl) { // T t = null; // try { // t = (T)cl.newInstance(); /* 只能调用public构造方法 */ // } catch(Exception e) { // e.printStackTrace(); // } // return t; //} @SuppressWarnings("unchecked") public static <T> T newInstance(Class<?> cl) { T t = null; try { Constructor<?> constructor = cl.getDeclaredConstructor(); constructor.setAccessible(true); t = (T)constructor.newInstance(); } catch(Exception e) { e.printStackTrace(); } return t; } @SuppressWarnings("unchecked") public static <T> T newInstance(Class<?> cl, Class<?>...args) { T t = null; try { Constructor<?> constructor = cl.getDeclaredConstructor(args); constructor.setAccessible(true); t = (T)constructor.newInstance("name", 1); } catch(Exception e) { e.printStackTrace(); } return t; } public static void main(String[] args) { Singleton instance = Single.INSTANCE; Singleton s1 = newInstance(Singleton.class, String.class, int.class); Singleton s2 = newInstance(Singleton.class, String.class, int.class); //Tmp t1 = newInstance(Tmp.class); //Tmp t2 = newInstance(Tmp.class); //System.out.println(s1 == s2); //System.out.println(t1 == t2); } }
Joshua Bloch (Effect java 作者)提出一种新的方法实现单例模式,就是使用Enum(其实也是类,一种特殊的类,构造方法必须是private ,final的)!如下:
enum Singleton { INSTANCE; public void sayHi() { System.out.println("Hi"); } }
这样当试图通过反射机制创建对象时,会抛出异常!
从java7开始在concurrent包中加入了Phaser类,它几乎可以取代CountDownLatch和CyclicBarrier, 其功能更灵活,更强大,支持动态调整需要控制的线程数。下面以一个具体实例说明这个Phaser类的用处,相信理解这个例子后,其功能不言而喻。
例子:有若干考生参加考试,考试科目是统一的,考试顺序为语文、数学、英语共三门,若其中一门挂科,则不能参加后面的考试。现在假设考试时间不受限制,开考时间以最后一名考完上一科时间为准,即先考完的考生并且成绩合格,需要参加下一门考试,则必须等待所有考生考完当科。
import java.util.*; import java.util.concurrent.*; class Examinee implements Runnable { public static Random random = new Random(new Date().getTime()); private String name; private Phaser phaser; private int english; private int chinese; private int math; public Examinee(String name, Phaser phaser) { this.name = name; this.phaser = phaser; } public boolean english() { int time = random.nextInt(10); try { TimeUnit.SECONDS.sleep(time); /* 考试时间是一个随机数 */ } catch (Exception e) { e.printStackTrace(System.err); } english = random.nextInt(101) ; /* 假定考试成绩是随机的 */ if (english < 60) { System.out.printf("%s Failed to pass English!\n", name); phaser.arriveAndDeregister(); /* 考试不合格,取消考试资格 */ return false; } else { phaser.arriveAndAwaitAdvance(); /* 通过考试,可以进行下一门考试 */ return true; } } public boolean chinese() { int time = random.nextInt(10); try { TimeUnit.SECONDS.sleep(time); } catch (Exception e) { e.printStackTrace(System.err); } chinese = random.nextInt(101) ; if (chinese < 60) { System.out.printf("%s Failed to pass Chinese!\n", name); phaser.arriveAndDeregister(); return false; } else { phaser.arriveAndAwaitAdvance(); return true; } } public boolean math() { int time = random.nextInt(10); try { TimeUnit.SECONDS.sleep(time); } catch (Exception e) { e.printStackTrace(System.err); } math = random.nextInt(101) ; if (math < 60) { System.out.printf("%s Failed to pass Math!\n", name); phaser.arriveAndDeregister(); return false; } else { phaser.arriveAndAwaitAdvance(); return true; } } public void showInfo() { System.out.printf("%s\tChinese:%d\tEnglish:%d\tMath:%d\n", name, chinese, english, math); phaser.arriveAndDeregister(); } @Override public void run() { phaser.arriveAndAwaitAdvance(); /* 等待考生做好准备,即在同一个起跑线上 */ System.out.printf("%s is ready!\n", name); if (!chinese() || !english() || !math()) return; showInfo(); /* 只显示所有科目均合格的考生分数 */ } } public class PhaserDemo2 { public static void main(String[] args) { final String[] names = {"Jim", "Hary", "Mary", "Jhon", "Ali", "Lucy", "Wang", "Zhang", "Li", "Uhm", "Hey", "Gu", "Asu", "Who"}; Phaser phaser = new Phaser(names.length); List<Thread> threads = new ArrayList<>(names.length); for (String name : names) { threads.add(new Thread(new Examinee(name, phaser), name)); /* 创建各个考生线程 */ } for (Thread t : threads) { t.start(); /* 开始考试 */ } for (Thread t : threads) { try { t.join(); /* 等待考试结束 */ } catch (InterruptedException e) { e.printStackTrace(System.err); } } System.out.printf("Terminal ? %s\n", phaser.isTerminated()); } }
由上面的例子,可以看出Phaser的功能。其中arrive方法很像CountDownLatch的countDown方法和CyclicBarrier的await方法,请自行比较之!Phaser类还有很多其他的功能,可以查看其API文档。
java.util.concurrent包中有许多涉及多线程并发的工具,使用这些现成工具可以大大简化编码,并且安全高效。
Exchanger提供了一个同步点,即调用exchange方法,当两个线程都到达这个同步点时,两个线程交换数据。
比如,有两个人,一个人需要买一本书,另一个人当然卖一本书,这两个人约定在某地交易,但这两个人距离约定地不同,到达目的地的时间也不同,先到的必须等待直到另一个人到达完成交易。
使用Exchanger类可以很容易实现!
import java.util.*; import java.util.concurrent.*; interface Exchangable { } class Money implements Exchangable { private int value; public Money(int value) { this.value = value; } public String toString() { return "$" + value; } } class Book implements Exchangable { private int value; private String name; public Book(String name, int value) { this.name = name; this.value = value; } public String toString() { String format = "Book: %s($%d)"; return String.format(format, name, value); } } class Seller implements Runnable { Exchanger<Exchangable> exchanger; Exchangable book; public Seller(Exchanger<Exchangable> exchanger) { this.exchanger = exchanger; this.book = new Book("Love Java", 88); } public void run() { try { Random random = new Random(System.currentTimeMillis()); int seconds = random.nextInt(10) + 1; System.out.println("Hi, I am Seller, I am going to sell a book,waiting " + seconds + "s"); TimeUnit.SECONDS.sleep(seconds); System.out.println("Hi, I am Seller, I get " + exchanger.exchange(book)); } catch (Exception e) { } } } class Buyer implements Runnable { Exchanger<Exchangable> exchanger; Exchangable money; public Buyer(Exchanger<Exchangable> exchanger) { this.exchanger = exchanger; this.money = new Money(88); } public void run() { try { Random random = new Random(System.currentTimeMillis() + 99); int seconds = random.nextInt(10) + 1; System.out.println("Hi, I am Buyer, I am going to buy a book,waiting " + seconds + "s"); TimeUnit.SECONDS.sleep(seconds); System.out.println("Hi, I am Buyer, I get " + exchanger.exchange(money)); } catch (Exception e) { } } } public class ExchangerDemo { public static void main(String[] args) { Exchanger<Exchangable> exchanger = new Exchanger<>(); Seller seller = new Seller(exchanger); Buyer buyer = new Buyer(exchanger); new Thread(seller).start(); new Thread(buyer).start(); } }
Exchanger类使用很简单,只有两个public方法,一个是exchange(V x)方法,死等待直到完成交易,另一个是exchange(V x, long time, TimeUnit timeunit),其中x是要交换的数据,time是时间,timeunit指定时间单位,合起来表示最长等待时间,比如exchange(x, 5, TimeUnit.SECONDS)表示最长等待5秒!
ubuntu13.10或以下版本也可以编译,只是源中maven和protobuf都版本太低,需要自己下源码重新编译安装,很繁琐。
hadoop2.5有编译好的发布包,但是默认是64位的,老是出现以下错误
OpenJDK Server VM warning: You have loaded library /hadoop/lib/native/libhadoop.so.1.0.0 which might have disabled stack guard. The VM will try to fix the stack guard now. It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'. 14/08/19 21:07:42 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
看起来好像只是警告无所谓,但在启动脚本中NAMENODES=$(hdfs getconf -namenodes),这时会把所有以上输出以空格作为分隔符作为namenode,显然出错,而且没法加载本地库,所以必须进行重新build。
网上很多说修改HADOOP_OPTS可以解决以上问题,但我试过很多次,根本不行,其实想想也是不可能的,64位的库怎么可以运行在32位呢!
从官方http://www.apache.org/dyn/closer.cgi/hadoop/core/下载源码。
源码是使用maven进行管理的,要求版本必须是maven3以上,由于maven调用了ant,因此ant也是必要的。
另外本地库使用c写的,还要求有cmake,make,auto工具,否则无法编译。总结以下是必要的包:
java中有各种类型的map,每种类型的map实现方式不同,但接口都类似。所有的具体map类型必须实现Map接口或者其子类,AbstractMap实现了Map接口,并实现了Map接口的部分功能,因此一般具体Map类型只需继承AbstractMap就可以了。
1. HashMap
HashMap的实现方式是一个table数组,数组元素是一个Entry类型(java8 中是Node类型,Entry变成了一个接口),Entry是一个内部类,类似c++的pair类型,b实际保存key 和value值,当put一个值,即调用put时,先计算key的hash值,通过这个hash值找到对应的table数组索引,如果hash冲突,采用的是拉链法解决冲突,即把所有同义词(hash值相等)存储在一个单链表中,因此Entry可以看作是一个单链表,next指向下一个同义词。一个好的hash应该尽量减少同义词,因为当出现hash冲突时,在同义词中查找是线性查找,效率很低。put方法代码为(java 7):
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) /* null的话直接存放在table[0] */ return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); /*找到table索引 */ for (Entry<K,V> e = table[i]; e != null; e = e.next) { /* 查找是否已经存在相同的key */ Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { /* 如果hash冲突且key一样,则返回旧值,替换成新值 */ V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); //插入新的Entry return null; /* 没有替换,则返回null */ }
从源码中可以看出HashMap允许null作为key值,相当于null的hash为0。当key相同时,则覆盖原来的值,并返回旧的value。判断key是否相同是调用key的equals,因此如果使用自己的类作为key,必须重载equals方法,否则引用相同时才相等。求hash值并不是直接使用key的hashCode方法,而是自己内部实现的hash方法,hash方法会调用key的hashCode方法,这样能够减少hash冲突,且保证hash值能在指定返回内,而hashCode返回的值有可能超出table的索引范围。因此自定义类要作为key还必须覆盖hashCode方法,尽量返回一个素数。
2. IdentityHashMap
上面说了HashMap的key值是否相同是调用equals方法,而IdentityHashMap原理类似,但判断key是否相同运用 == 运算符,即判断是否同一个引用,实现上也有些不同,table不再是Entry,而是一个Object对象数组(不会存在hash冲突)。put时先计算key的hash值,通过hash值找到table数组的索引i,然后table[i]存储key,table[i + 1]存储value,代码为:
public V put(K key, V value) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); Object item; while ( (item = tab[i]) != null) { if (item == k) { V oldValue = (V) tab[i + 1]; tab[i + 1] = value; return oldValue; } i = nextKeyIndex(i, len); } modCount++; tab[i] = k; tab[i + 1] = value; if (++size >= threshold) resize(len); // len == 2 * current capacity. return null; }
因此IdentityHashMap用于存储key值重复的map。
3. Hashtable
Hashtable和HashMap类似,不过put方法是同步的,使用synchronized锁住整个方法,支持并发控制,但现在已经废弃了,因为效率太低了,取而代之的是ConcurrentHashMap。
4. ConcurrentHashMap
取代Hashtable,和Hashtable不同,它是使用段加锁的方法实现并发,而不是同步整个方法。和HashMap不同,HashMap所有的Entry保存在一个table数组中,而ConcurrentHashMap是保存在不同的Segment(段)中,通过hash值先定位到Segment(其实也是一个Segment数组),然后只需对这个Segment(继承了ReentrantLock)加锁就可以了,不影响其他Segment。
5.LinkedHashMap
LinkedHashMap是HashMap的子类,因此具有HashMap的所有特点。与HashMap不同的是,HashMap不记录顺序,迭代时不保证任何顺序,而LinkedHashMap Entry使用了一个双链表记录顺序,并且Entry多了before 和after指针,迭代默认根据插入的顺序,可以设置成按最近访问顺序,put效率会比HashMap低,毕竟会有记录顺序的开销,但迭代速度会更快,直接遍历整个双向链表即可。
6. EnumMap
EnumMap使用java Enum类型作为key,相对简单且高效,内部还是使用了table,table的值是Object类型,put时就是根据key的值(原始值,一个整型数)作为索引,直接插入,table的长度就是key的枚举个数,其put代码为:
public V put(K key, V value) { typeCheck(key); int index = key.ordinal(); Object oldValue = vals[index]; vals[index] = maskNull(value); if (oldValue == null) size++; return unmaskNull(oldValue); }
首先是类型检查,判断key是否是初始化的枚举类型(构造方法必须指定枚举类型的类类型),如果value是null则映射成内部的NULL(也是一个Object对象)。由此可见EnumMap是非常高效的,插入删除访问都是O(1),不存在冲突问题。
7. TreeMap
TreeMap和之前的Map类型使用的数据结构不一样,它实现的是SortedMap,即按照key值自然顺序排列的,内部使用的数据结构是红黑树,红黑树是一种类平衡二叉树(和传统严格的平衡树有点差别),put源码为:
public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { compare(key, key); // type (and possibly null) check root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; if (cpr != null) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } else { if (key == null) throw new NullPointerException(); Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } Entry<K,V> e = new Entry<>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; fixAfterInsertion(e); size++; modCount++; return null; }
首先判断根是否空,不为空则遍历红黑树,看是否已经存在key,存在则直接覆盖原来的值并返回旧值,否则插入新数据,并调整红黑树。
8. ConcurrentSkipListMap
ConcurrentSkipListMap也是默认根据key值排序的,并且支持高并发访问(比ConcurrentHashMap并发性能更好,存取速度相对比ConcurrentHashMap低)。不过实现并不是使用红黑树,而是跳表,跳表的性能和查找树性能相当。
题外话:
以上介绍了8中map,如何选择取决于实际应用,如果不关心顺序且是单线程操作,使用HashMap,如果需要key按顺序排列,则使用TreeMap, 如果需要关心插入顺序或者最近访问顺序,使用LinkedHashMap。当涉及并发访问时,不关心key顺序使用ConcurrentHashMap,高并发且关心key顺序,使用ConcurrentSkipListMap,尽量不要使用Hashtable。如果key是枚举类型,则使用EnumMap。另外几乎每个Map类型都有相对应的Set版本,Set一般就是继承自Map,Set的元素值作为Map的key,而Map的value默认为一个Object对象常量,当然其实Map的value是什么都无所谓。
java反射机制可以获取一个对象的所有属性以及其值,甚至可以改变其值,包括私有属性。利用这个特性可以使用json创建一个对象,尤其是java bean,非常方便。比如有一个对象有属性:id, name,有一个json对象{"id":"1000", "name" : "test"},通过反射机制可以很随意的创建一个和json对象值一样的对象,实现json对象和java对象转化。
首先需要获取一个对象的所有属性,注意到java Class对象方法getFields方法返回的是所有公共属性,包括父类和接口的属性,而getDeclaredFields()方法获取的是所有属性,包括私有属性,但不包含父类的属性。而要获取所有的包括父类的属性,没有现成的接口,于是自己写了个简单的,可能有问题,仅用于测试。
public static Set<Field> getFields(Object obj) { Set<Field> fields = new HashSet<Field>(); Class<? extends Object> cl = obj.getClass(); String objName = Object.class.getName(); while (!cl.getName().equals(objName)) { fields.addAll(Arrays.asList(cl.getDeclaredFields())); cl = cl.getSuperclass(); } return fields; }
获取了所有的属性后,就可以通过属性设置新的值,代码为:
public static boolean setValue(Object obj, Field field, Object value) { field.setAccessible(true); try { field.set(obj, value); } catch (IllegalArgumentException | IllegalAccessException e) { // fixed here } return true; }
于是整个通过json新建对象代码为(这里使用了org.json包):
public Entity(JSONObject entity) { Set<Field> fields = Reflect.getFields(this); for (Field field : fields) { String _field = field.getName(); if (entity.has(_field)) { Reflect.setValue(this, field, entity.get(_field)); } } }
通过遍历所有的属性以及Json对象中存在的键值,设置对象的值。这主要在初始化java bean时非常有用。
java 从1.1就开始支持反射机制了,c++不支持反射机制。通过反射机制能够获取对象的类型和这个对象能做什么(有什么属性,什么方法),能够动态调用对象的方法,设置属性,以及创建新的实例(因此除了利用new关键字创建实例,还可以通过反射机制创建实例,其实还有两种方法,即反序列化和clone)。利用java的反射机制可以实现一些疯狂的事,做一些貌似无法实现的事。本文主要参照了2009年JavaZone会议上Heinz Kabutz发表的关于反射技术的疯狂演说 ,写得不错!
我们知道java的private属性是不允许外部直接访问修改的,而只能通过调用对象的外部接口访问和修改,但这不是绝对的,利用反射机制可以不调用对象的公共方法而修改对象属性,甚至修改final属性的值。
java String中字符存储是使用了private final char[] value,从这里我们也可以看出为什么java 字符串是不允许修改的,因为value是final的,但这仅意味着你无法通过调用它的公有方法来改变它的值,利用反射机制可以绕过这些限制而修改它,如代码:(想想这段代码的输出会是什么呢?)
import java.lang.reflect.*; public class Test { public static void main(String[] args) throws Exception { Field field = String.class.getDeclaredField("value"); field.setAccessible(true); field.set("hello!", "cheers".toCharArray()); System.out.println("hello!"); } }
这里还有一个问题是字符串池,当使用多个字面字符串时(不使用new String("...")),如果已经存在这个字符串,则不会创建一个新的对象,而仅仅指向已存在字符串的引用,即
String s1 = "hello"; String s2 = "hello"; System.out.println(s1 == s2);
s1 和s2是同一个对象的引用,即s1 == s2返回true。
回到上一段代码,field.set("hello!", "cheers".toCharArray()) 和 System.out.println("hello!")中字符串"hello!"其实是同一个对象,利用反射机制改变这个对象的value值,因此此时字符串"hello!"对应的value其实已经变成了"cheers",因此上段代码输出为cheers而不是hello!。
还有更神奇的,猜猜下面这段代码的输出结果是什么?
import java.lang.reflect.*; public class Test { public static void main(String[] args) throws Exception { Field field = null; field = Integer.class.getDeclaredField("value"); field.setAccessible(true); field.set(42, 43); System.out.printf("6 * 7 = %d\n", 6 * 7); } }
利用反射机制还可以做很多奇妙的事,比如获取方法调用者,获取类中的方法,获取对象的内存大小等等,具体参照原文!
教科书都会说switch只能用于整型变量,于是c语言中很显然int, short, long, enum, char(包括无符号类型和long long)都可以作为switch变量,而float,double,struct,union显然不可以,那指针可不可以呢? 指针本质上就是一个无符号整数,根据定义应该是可以的,但在c语言中是非法的!即int i = 0; int *j = &i; switch(j){}是非法的(至少gcc是非法).
java中同样有switch语法,如果我们照般c语言的,很显然可接受的类型为int, byte, short, enum, char, long。我们觉得理应就是这样的,以至于我们不需要再作任何解释。可是当我们声明了一个long类型的变量,却发现我们怎么也编译不过去!是的,java的switch不支持long类型,这我也不理解这其中的缘由。那对象是否支持呢?也许你会很果断的回答,不支持,因为它不是整型,更确切的说不是int类型!可是事实并非如此,其实也不难理解,由于Integer, Byte等具有自动拆箱功能,因此可以作为switch变量也就不足为奇了。 好了,感觉所有的问题都解决了。但在java7中,switch变量可以传递String对象,这是java7中新增的特性。于是我是否可以作个这样的假定,switch可以传递任何对象,类似System.out.println函数,会自动调用对象的toString()方法呢? 事实上不可以,这样做是合理的。