Java基础笔记

对动态代理&AOP的理解

动态代理,我的理解就是对调用对象的一个封装。InvocationHandle可以自定义invoke方法内容,这样就可以在被代理类方法前后加入一些逻辑,比如进行一些过滤啥的,类似装饰器、过滤器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
定义代理接口
public interface Foo {
String doSomething();
}

public class FooImpl implements Foo {
@Override
public String doSomething() {

return "finished";
}
}

// 定义 InvocationHandler,target 为被代理对象的引用,在方法执行完后打印耗时
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TimingInvocationHandler implements InvocationHandler {
private Object target;

public TimingInvocationHandler(Object target) {
this.target = target;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long start = System.nanoTime();
Object result = method.invoke(target, args);
long elapsed = System.nanoTime() - start;

System.out.println(String.format("Executing %s finished in %d ns",
method.getName(),
elapsed));

return result;
}
}

//Usage
public class DynamicProxyTest {
@Test
public void test() {
ClassLoader cl = DynamicProxyTest.class.getClassLoader();
Class[] interfaces = new Class[]{Foo.class};
FooImpl fooImpl = new FooImpl();
InvocationHandler timingInvocationHandler = new TimingInvocationHandler(fooImpl);
Foo foo = (Foo) Proxy.newProxyInstance(cl, interfaces, timingInvocationHandler);
foo.doSomething();
}
}

java双亲委派学习

https://www.cnblogs.com/Genesisx/p/9260266.html https://blog.csdn.net/u010841296/article/details/89731566

“java平台通过委派模型去加载类。每个类加载器都有一个父加载器。当需要加载类时,会优先委派当前所在的类的加载器的父加载器去加载这个类。如果父加载器无法加载到这个类时,再尝试在当前所在的类的加载器中加载这个类。” 如果整个链路中,父加载器都没有加载这个类,且无法加载这个类时,才会由Test.class所在的加载器去加载某个类

注意: Class.forName除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。 而classloader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

手工编译java单文件

javac -cp commons-collections-3.2.1.jar TransformTest.java java -cp commons-collections-3.2.1.jar;./ TransformTest

http://www.mamicode.com/info-detail-2384550.html

手工编译java文件为jar

首先需要按照如下结构。
dir:
│ RMIexploit.java
│
├─lib
│      commons-collections-3.2.1.jar
│
└─src
    │  MANIFEST.MF
    │
    └─com
        └─exp
                RMIexploit.class

lib中放依赖的jar,src中放MANIFEST.MF和建的包,比如图中是com.exp,包是要写在.java里的,package com.exp; .class是用上边的命令编译好的字节码,放入包中就行。

MANIFEST.MF的文件内容如下:

Manifest-Version: 1.0
Created-By: ShadowGlint
Class-Path: ../lib/commons-collections-3.2.1.jar //第三发jar包,每个之间都要用空格隔开, 包括第一个与Class-Path 关键字之间
Main-Class: com.exp.RMIexploit  //main方法路径,即 package路径加类名 ,注意main-class后有一个空格
                                //最后一定要空一行

注意对应好各种依赖的名字,然后src目录下打包: jar -cvfm RMIexploit.jar MANIFEST.MF ./*

然后java -jar RMIexploit.jar执行就好。

参考链接:https://www.cnblogs.com/Actexpler-S/p/9625763.html

ArrayList初始化

ArrayList<String> list = new ArrayList<String>(arrays.asList("1","2"));

java 泛型学习

貌似去掉也行。。 泛型是编译时的,运行时都是类型擦出后的Object public class TestDemo { public static void main(String[] args) { Message message = new Message() ; message.setMessage(55); fun(message); } public static void fun(Message

temp){
System.out.println(temp.getMessage());
}
}

class Message<T> {
    private T message ;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}

<?>有没有都能过

java接口

接口只能public

接口中的方法也只能public,所以干脆不写,直接Integer find(){}

接口中的静态方法必须有方法体,比如 public static Integer find(){}

接口中的属性必须是 public static final

java HashMap学习笔记

这篇无敌了!!!! https://www.cnblogs.com/yuanblog/p/4441017.html

首先,贴get和put的代码

public V put(K key, V value) {
    // HashMap允许存放null键和null值。
    // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
    if (key == null)
        return putForNullKey(value);
    // 根据key的keyCode重新计算hash值。
    int hash = hash(key.hashCode());
    // 搜索指定hash值在对应table中的索引。
    int i = indexFor(hash, table.length);
    // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            // 如果发现已有该键值,则存储新的值,并返回原始值
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    // 如果i索引处的Entry为null,表明此处还没有Entry。
    modCount++;
    // 将key、value添加到i索引处。
    addEntry(hash, key, value, i);
    return null;
}
public V get(Object key) {
    if (key == null)
        return getForNullKey();
    int hash = hash(key.hashCode());
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
        e != null;
        e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
            return e.value;
    }
    return null;
}

HashMap是通过拉链法实现的散列表,即数组和链表的结合体。表现在HashMap包括许多的Entry,而每一个Entry本质上又是一个单向链表。 HashMap底层就是一个数组,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。Entry就是数组中的元素,每个Map.Entry其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表。

transient Entry[] table;

static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;
    final int hash;
    ……
}

根据hash值得到这个元素在数组中的位置(下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

我们可以看到在HashMap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。如何计算这个位置就是hash算法。前面说过HashMap的数据结构是数组和链表的结合,所以我们当然希望这个HashMap里面的元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表,这样就大大优化了查询的效率。

根据上面put方法的源代码可以看出,当程序试图将一个key-value对放入HashMap中时,程序首先根据该key的hashCode()返回值决定该Entry的存储位置:如果两个Entry的key的hashCode()返回值相同,那它们的存储位置相同。如果这两个Entry的key通过equals比较返回true,新添加Entry的value将覆盖集合中原有Entry的value,但key不会覆盖。如果这两个Entry的key通过equals比较返回false,新添加的Entry将与集合中原有Entry形成Entry链,而且新添加的Entry位于Entry链的头部。通过这种方式就可以高效的解决HashMap的冲突问题。

HashMap的resize

首先是初始容量和加载因子的概念。初始容量是数组初始长度(容量又叫桶),加载因子是是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash操作,从而哈希表将具有大约两倍的桶数。

默认情况下,数组大小为16,那么当hashmap中元素个数超过16_0.75=12的时候,就把数组的大小扩展为2_16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.75*1000 < 1000, 也就是说为了让0.75 * size > 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题。

ReHash在并发的情况下可能会形成链表环。这是hashmap线程不安全造成的

resize过程见图:

HashMap的遍历

  1. 遍历键值对

    // 假设map是HashMap对象
    // map中的key是String类型,value是Integer类型
    Integer integ = null;
    Iterator iter = map.entrySet().iterator();
    while(iter.hasNext()){

    Map.Entry entry = (Map.Entry)iter.next();
    key = (String)entry.getKey();
    integ = (Integer)entry.getValue();

    }

  2. 遍历键/value

    Iterator iter = map.keySet().iterator();

    integ = (Integer)map.get(key);

java中几种Map总结

HashMap 是基于“拉链法”实现的散列表。一般用于单线程程序中。 Hashtable 也是基于“拉链法”实现的散列表。它一般用于多线程程序中。 WeakHashMap 也是基于“拉链法”实现的散列表,它一般也用于单线程程序中。相比HashMap,WeakHashMap中的键是”弱键”,当”弱键”被GC回收时,它对应的键值对也会被从WeakHashMap中删除;而HashMap中的键是强键。 TreeMap 是有序的散列表,它是通过红黑树实现的。它一般用于单线程中存储有序的映射。

Hashtable和HashMap

  1. Hashtable的几乎所有函数都是同步的,即它是线程安全的,支持多线程。 而HashMap的函数则是非同步的,它不是线程安全的。
  2. HashMap的key、value都可以为null。 Hashtable的key、value都不可以为null
  3. 遍历方式不同
  4. 初始容量和resize方式不同

线程安全&HashMap的三种同步方式

线程安全 为何会线程不安全? 多个线程同时访问一块内存数据,导致竞争、读写数据不一致等问题

为何不会进程不安全? 进程间不共享数据,堆栈是隔离的。所以不会有这个问题。

如何解决?

  1. 加锁,保证每个资源变量同时至多被一个线程占用。
  2. 让线程也拥有资源,不用去共享进程中的资源。

HashMap同步方式:

  1. synchronized
    1
    2
    3
    lock.lock(); 
    Value = map.get(key);
    lock.unlock();
  2. ConcurrentHashMap

Proudly powered by Hexo and Theme by Hacker
© 2021 LFY