Java随笔

Java String 字符串的HashCode值

public int hashCode()
  Returns a hash code for this string. The hash code for a String object is computed as:  s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 using int arithmetic, where s[i] is the ith character of the string, n is the length of the string, and ^ indicates exponentiation. (The hash value of the empty string is zero.) 

查看 HashTable源码解析 从中可知,Hashtable 是使用了 相同的hash值在同一个链表。判断两个element是否相同,首先判断hash值是否相等,然后equals判断是否相同。根据hash值来得到elmement中的下标:(hash & 0x7FFFFFFF ) % tab.length 。在Hashtable的源码实现中,还有几个地方比较值得注意,(1)loadFactor,  这个参数决定了hashtable自动调整大小,当超过loadFactor时,自动reHash(), 默认值是0.75f, 而rehash()会自动扩大原来capacity一倍。(2)rehash() 时将原来的相同Hash的链表直接链到新表中,但是最新值放在第一位,即自动保留第一位给将来新插入的值。(3)要常使用containsKey() 而不是contains和containsValue, 后两者都判断是否value存在,要经过两次遍历,而containsKey是判断key是否一致,很快。  下面是典型的put方法


 /**
     * 向hashtable中添加元素。
     * 加锁。 
     */
    public synchronized V put(K key, V value) {
    // Make sure the value is not null
    // 如果value为null,则抛出NullPointerException异常。
    if (value == null) {
        throw new NullPointerException();
    }
 
    // Makes sure the key is not already in the hashtable.
    // 判断hashtable中是否已存在key。
    Entry tab[] = table;
    // 此处看出key也不可以是null,否则也会抛出NullPointerException异常。
    int hash = key.hashCode();
    // 获取key对应在数组中的下标。
    int index = (hash & 0x7FFFFFFF) % tab.length;
    // 遍历此下标上的链表,判断是否已存在key,如果存在则覆盖value值,并返回旧值。
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
        V old = e.value;
        e.value = value;
        return old;
        }
    }
 
    // 如果不存在,则要添加新的Entry,hashtable的结构发生改变,modCount自增。
    modCount++;
    // 如果count大于或者等于界限值,则执行refresh()方法来扩容。
    if (count >= threshold) {
        // Rehash the table if the threshold is exceeded
        rehash();
 
            tab = table;
            // 扩容完成后,再取得此key对应在新数组中的下标。
            index = (hash & 0x7FFFFFFF) % tab.length;
    }
 
    // Creates the new entry.
    // 创建新的Entry,并把它添加到链表的第一位。
    Entry<K,V> e = tab[index];
    tab[index] = new Entry<K,V>(hash, key, value, e);
    count++;
    // 创建成功则返回null。
    return null;
    }
 
    /**
     * 从hashtable中删除给定的key对应的Entry。
     * 加锁。
     */
    public synchronized V remove(Object key) {
    Entry tab[] = table;
    int hash = key.hashCode();
    // 获取key在数组中对应的下标。
    int index = (hash & 0x7FFFFFFF) % tab.length;
    // 遍历下标上的链表。
    for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
        // 判断key是否相等。
        if ((e.hash == hash) && e.key.equals(key)) {
        // 如果相等,则执行删除操作,结构发生改变,modCount自增。
        modCount++;
        // 如果被删除的不是该链表的第一位,则将前面Entry的next属性指向后面Entry,跳过此
        // 将被删除的Entry。
        if (prev != null) {
            prev.next = e.next;
        } else {
            // 如果被删除的是链表的第一位,则将后一位Entry赋值给此下标上的值。
            tab[index] = e.next;
        }
        count--;
        V oldValue = e.value;
        // 将value设为null,等待垃圾回收。
        e.value = null;
        // 返回被删除的Entry(value已为null)。
        return oldValue;
        }
    }
    // 如果hashtable中不存在此key,则直接返回null。
    return null;
    }
 


TreeSet虽然是set但是底层实现完全是借住TreeMap实现的,而TreeMap底层是一棵红黑树, 这里有一篇介绍TreeMap红黑树不错的文章:TreeMap源码解析


Java的内部类分为四种,static inner class, member inner calss, local inner class, anonymous inner class四种,前两种都可以使用类修饰符,如public和static,而第三种不能使用修饰符,且只能访问外部类中的final对象变量,local inner class 和anonymous class 是在方法中定义的。 

为啥只能访问final呢?如果不使用final将导致:局部变量的生命周期与局部内部类的对象的生命周期的不一致性!

设方法f被调用,从而在它的调用栈中生成了变量i,此时产生了一个局部内部类对象inner_object,它访问了该局部变量i .当方法f()运行结束后,局部变量i就已死亡了,不存在了.但:局部内部类对象inner_object还可能   一直存在(只能没有人再引用该对象时,它才会死亡),它不会随着方法f()运行结束死亡.这时:出现了一个"荒唐"结果:局部内部类对象inner_object要访问一个已不存在的局部变量i!


Java中的值传递和引用传递

当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 
    答:是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。

Java参数,不管是原始类型还是引用类型,传递的都是副本(有另外一种说法是传值,但是说传副本更好理解吧,传值通常是相对传址而言)。

    如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的。如果在函数中改变了副本的 值不会改变原始的值.
    如果参数类型是引用类型,那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。如果在函数中没有改变这个副本的地址,而是改变了地址中的 值,那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址,如new一个,那么副本就指向了一个新的地址,此时传入的参数还是指向原来的 地址,所以不会改变参数的值。




JDK动态代理中包含一个类和一个接口: 
InvocationHandler接口: 
public interface InvocationHandler { 
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 

参数说明: 
Object proxy:指被代理的对象。 
Method method:要调用的方法 
Object[] args:方法调用时所需要的参数 

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。 

Proxy类: 
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法: 
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) 
                               throws IllegalArgumentException 
参数说明: 
ClassLoader loader:类加载器 
Class<?>[] interfaces:得到全部的接口 
InvocationHandler h:得到InvocationHandler接口的子类实例 

Ps:类加载器 
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器; 
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的; 
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类; 
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。 

动态代理 
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。 

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

To create a proxy for some interface Foo :
 InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });
Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });
 or more simply:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class },handler);
 A dynamic proxy class (simply referred to as a proxy class below) is a class that implements a list of interfaces specified at runtime when the class is created, with behavior as described below.

Proxy类中的关于getProxyClass方法和newProxyInstance方法

 public static Class<?> getProxyClass(ClassLoader loader,  Class<?>... interfaces)
	throws IllegalArgumentException
    {
	if (interfaces.length > 65535) {
	    throw new IllegalArgumentException("interface limit exceeded");
	}

	Class proxyClass = null;

	/* collect interface names to use as key for proxy class cache */
	String[] interfaceNames = new String[interfaces.length];

	Set interfaceSet = new HashSet();	// for detecting duplicates

	for (int i = 0; i < interfaces.length; i++) {
	    /*
	     * Verify that the class loader resolves the name of this
	     * interface to the same Class object.
	     */
	    String interfaceName = interfaces[i].getName();
	    Class interfaceClass = null;
	    try {
		interfaceClass = Class.forName(interfaceName, false, loader);
	    } catch (ClassNotFoundException e) {
	    }
	    if (interfaceClass != interfaces[i]) {
		throw new IllegalArgumentException(
		    interfaces[i] + " is not visible from class loader");
	    }

	    /*
	     * Verify that the Class object actually represents an
	     * interface.
	     */
	    if (!interfaceClass.isInterface()) {
		throw new IllegalArgumentException(
		    interfaceClass.getName() + " is not an interface");
	    }

	    /*
	     * Verify that this interface is not a duplicate.
	     */
	    if (interfaceSet.contains(interfaceClass)) {
		throw new IllegalArgumentException(
		    "repeated interface: " + interfaceClass.getName());
	    }
	    interfaceSet.add(interfaceClass);

	    interfaceNames[i] = interfaceName;
	}

	/*
	 * Using string representations of the proxy interfaces as
	 * keys in the proxy class cache (instead of their Class
	 * objects) is sufficient because we require the proxy
	 * interfaces to be resolvable by name through the supplied
	 * class loader, and it has the advantage that using a string
	 * representation of a class makes for an implicit weak
	 * reference to the class.
	 */
	Object key = Arrays.asList(interfaceNames);

	/*
	 * Find or create the proxy class cache for the class loader.
	 */
	Map cache;
	synchronized (loaderToCache) {
	    cache = (Map) loaderToCache.get(loader);
	    if (cache == null) {
		cache = new HashMap();
		loaderToCache.put(loader, cache);
	    }
	    /*
	     * This mapping will remain valid for the duration of this
	     * method, without further synchronization, because the mapping
	     * will only be removed if the class loader becomes unreachable.
	     */
	}

	/*
	 * Look up the list of interfaces in the proxy class cache using
	 * the key.  This lookup will result in one of three possible
	 * kinds of values:
	 *     null, if there is currently no proxy class for the list of
	 *         interfaces in the class loader,
	 *     the pendingGenerationMarker object, if a proxy class for the
	 *         list of interfaces is currently being generated,
	 *     or a weak reference to a Class object, if a proxy class for
	 *         the list of interfaces has already been generated.
	 */
	synchronized (cache) {
	    /*
	     * Note that we need not worry about reaping the cache for
	     * entries with cleared weak references because if a proxy class
	     * has been garbage collected, its class loader will have been
	     * garbage collected as well, so the entire cache will be reaped
	     * from the loaderToCache map.
	     */
	    do {
		Object value = cache.get(key);
		if (value instanceof Reference) {
		    proxyClass = (Class) ((Reference) value).get();
		}
		if (proxyClass != null) {
		    // proxy class already generated: return it
		    return proxyClass;
		} else if (value == pendingGenerationMarker) {
		    // proxy class being generated: wait for it
		    try {
			cache.wait();
		    } catch (InterruptedException e) {
			/*
			 * The class generation that we are waiting for should
			 * take a small, bounded time, so we can safely ignore
			 * thread interrupts here.
			 */
		    }
		    continue;
		} else {
		    /*
		     * No proxy class for this list of interfaces has been
		     * generated or is being generated, so we will go and
		     * generate it now.  Mark it as pending generation.
		     */
		    cache.put(key, pendingGenerationMarker);
		    break;
		}
	    } while (true);
	}

	try {
	    String proxyPkg = null;	// package to define proxy class in

	    /*
	     * Record the package of a non-public proxy interface so that the
	     * proxy class will be defined in the same package.  Verify that
	     * all non-public proxy interfaces are in the same package.
	     */
	    for (int i = 0; i < interfaces.length; i++) {
		int flags = interfaces[i].getModifiers();
		if (!Modifier.isPublic(flags)) {
		    String name = interfaces[i].getName();
		    int n = name.lastIndexOf('.');
		    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
		    if (proxyPkg == null) {
			proxyPkg = pkg;
		    } else if (!pkg.equals(proxyPkg)) {
			throw new IllegalArgumentException(
			    "non-public interfaces from different packages");
		    }
		}
	    }

	    if (proxyPkg == null) {	// if no non-public proxy interfaces,
		proxyPkg = "";		// use the unnamed package
	    }

	    {
		/*
		 * Choose a name for the proxy class to generate.
		 */
		long num;
		synchronized (nextUniqueNumberLock) {
		    num = nextUniqueNumber++;
		}
		String proxyName = proxyPkg + proxyClassNamePrefix + num;
		/*
		 * Verify that the class loader hasn't already
		 * defined a class with the chosen name.
		 */

		/*
		 * Generate the specified proxy class.
		 */
		byte[] proxyClassFile =	ProxyGenerator.generateProxyClass(
		    proxyName, interfaces);
		try {
		    proxyClass = defineClass0(loader, proxyName,
			proxyClassFile, 0, proxyClassFile.length);
		} catch (ClassFormatError e) {
		    /*
		     * A ClassFormatError here means that (barring bugs in the
		     * proxy class generation code) there was some other
		     * invalid aspect of the arguments supplied to the proxy
		     * class creation (such as virtual machine limitations
		     * exceeded).
		     */
		    throw new IllegalArgumentException(e.toString());
		}
	    }
	    // add to set of all generated proxy classes, for isProxyClass
	    proxyClasses.put(proxyClass, null);

	} finally {
	    /*
	     * We must clean up the "pending generation" state of the proxy
	     * class cache entry somehow.  If a proxy class was successfully
	     * generated, store it in the cache (with a weak reference);
	     * otherwise, remove the reserved entry.  In all cases, notify
	     * all waiters on reserved entries in this cache.
	     */
	    synchronized (cache) {
		if (proxyClass != null) {
		    cache.put(key, new WeakReference(proxyClass));
		} else {
		    cache.remove(key);
		}
		cache.notifyAll();
	    }
	}
	return proxyClass;
    }
    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
	throws IllegalArgumentException
    {
	if (h == null) {
	    throw new NullPointerException();
	}

	/*
	 * Look up or generate the designated proxy class.
	 */
	Class cl = getProxyClass(loader, interfaces);

	/*
	 * Invoke its constructor with the designated invocation handler.
	 */
	try {
	    Constructor cons = cl.getConstructor(constructorParams);
	    return (Object) cons.newInstance(new Object[] { h });
	} catch (NoSuchMethodException e) {
	    throw new InternalError(e.toString());
	} catch (IllegalAccessException e) {
	    throw new InternalError(e.toString());
	} catch (InstantiationException e) {
	    throw new InternalError(e.toString());
	} catch (InvocationTargetException e) {
	    throw new InternalError(e.toString());
	}
    }


Proxy和InvocationHandler是JDK动态代理,而如果要使用cglib,要实现 MethodInterceptor 接口 来自Spring中的
http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html


http://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html



深度优先搜索:Depth First Search, 树的先根遍历

广度优先搜索:Breadth First Search, 树的按层次遍历

对象的序列化操作: FileOutputStream fs = new FileOutputStream("person.txt");  ObjectOutputStream os  = new ObjectOutputStream(fs);  os.writeObject(userList); 



public 和static的位置可以调换,

如何覆写equals()和hashcode


覆写equals方法

1  使用instanceof操作符检查“实参是否为正确的类型”。

2  对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。

3. 对于非float和double类型的原语类型域,使用==比较;

4  对于对象引用域,递归调用equals方法;

5  对于float域,使用Float.floatToIntBits(afloat)转换为int,再使用==比较;

6  对于double域,使用Double.doubleToLongBits(adouble)转换为int,再使用==比较;

7  对于数组域,调用Arrays.equals方法。

覆写hashcode

1. 把某个非零常数值,例如17,保存在int变量result中;

2. 对于对象中每一个关键域f(指equals方法中考虑的每一个域):

3, boolean型,计算(f? 0 : 1);

4. byte,char,short型,计算(int);

5. long型,计算(int)(f ^ (f>>>32));

6. float型,计算Float.floatToIntBits(afloat);

7. double型,计算Double.doubleToLongBits(adouble)得到一个long,再执行[2.3];

8. 对象引用,递归调用它的hashCode方法;

9. 数组域,对其中每个元素调用它的hashCode方法。

10. 将上面计算得到的散列码保存到int变量c,然后执行result=37*result+c;

11. 返回result。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值