美文网首页
java 技术规范编程

java 技术规范编程

作者: xuefeng_apple | 来源:发表于2021-06-11 15:18 被阅读0次

1-集合处理

1.1-ArrayList remove 操作
正确:

List<String> list = new ArrayList<>(); 
list.add("1"); 
list.add("2"); 
Iterator<String> iterator = list.iterator(); 
while (iterator.hasNext()) { 
    String item = iterator.next();   // 为啥这里进行next 操作,next 是获取当前值,请看源码分析
    if (删除元素的条件) { 
        iterator.remove(); 
    } 
}

有问题:

List<String> list = new ArrayList<>(); 
list.add("1"); 
list.add("2"); 
for (String item : list) {
    if ("1".equals(item)) {
        list.remove(item); 
    }
}

ArrayList的iterator 原理
libcore/ojluni/src/main/java/java/util/ArrayList.java
在remove 函数中,默认lastRet =-1,因此取当前值,也就是调用next 函数后lastRet = i, 这样就可以remove 了,

/**
     * Returns an iterator over the elements in this list in proper sequence.
     *
     * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     *
     * @return an iterator over the elements in this list in proper sequence
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

    /**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {  // 获取当前值
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);   ///lastRet 默认是-1 , remove 之前要调用next函数
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

1.2-entrySet 遍历 Map 类集合 KV
推荐采用entrySet方式进行get

public static void main(String[] args) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("1", "value1");
    map.put("2", "value2");
    map.put("3", "value3");
  
    //第一种:普遍使用,二次取值
    System.out.println("通过Map.keySet遍历key和value:");
    for (String key : map.keySet()) {
        System.out.println("key= "+ key + " and value= " + map.get(key));
    }
  
    //第二种
    System.out.println("通过Map.entrySet使用iterator遍历key和value:");
    Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<String, String> entry = it.next();
        System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
    }
  
    //第三种:推荐,尤其是容量大时
    System.out.println("通过Map.entrySet遍历key和value");
    for (Map.Entry<String, String> entry : map.entrySet()) {
        System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
    }
 
    //第四种
    System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
    for (String v : map.values()) {
        System.out.println("value= " + v);
    }
 }

2-并发处理

2.1 单例对象
获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
*2.2 线程名字
创建线程或线程池时请指定有意义的线程名称,尽量使用线程池。

public class TimerTaskThread extends Thread { 
    public TimerTaskThread() { 
         super.setName("TimerTaskThread");
    } 
 }

2.3 不安全的类的处理方式
SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为
static,必须加锁,或者使用 DateUtils 工具类.

private static final ThreadLocal<DateFormat> df = new   ThreadLocal<DateFormat>() {
    @Override
    protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd");
    }
}; 

对ThreadLocal的理解:
ThreadLocal 推荐使用static 修饰
实际上是ThreadLocal的静态内部类ThreadLocalMap为每个Thread都维护了一个数组table,ThreadLocal确定了一个数组下标索引 i,而这个下标就是value存储的对应位置

  • 对于某一ThreadLocal来讲,他的索引值i是确定的,在不同线程之间访问时访问的是不同的table数组的同一位置即都为table[i],只不过这个不同线程之间的table是独立的。
  • 对于同一线程的不同ThreadLocal来讲,这些ThreadLocal实例共享一个table数组,然后每个ThreadLocal实例在table中的索引i是不同的。比如:
//在某一线程声明了ABC三种类型的ThreadLocal
ThreadLocal<A> sThreadLocalA = new ThreadLocal<A>();
ThreadLocal<B> sThreadLocalB = new ThreadLocal<B>();
ThreadLocal<C> sThreadLocalC = new ThreadLocal<C>();
  • Synchronized是通过线程等待,牺牲时间来解决访问冲突
  • ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值.
    ThreadLocal参考

2.4 HashMap 使用注意
HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在
开发过程中可以使用其它数据结构或加锁来规避此风险。

2.5 ThreadPoolExecutor规则
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

ThreadPoolExecutor使用
需要进一步的补充

3 命名风格

  • 类名使用 UpperCamelCase 风格,
  • 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格
  • 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
  • 类型与中括号紧挨相连来表示数组。 例子:int[] arrayDemo;
  • 杜绝完全不规范的缩写,避免望文不知义。

4 其他

4.1class 类定义与Comparator 使用方式

文件名称是:MyClass.java, 可以看出一个文件定义了两个class类,MyClassTest 不能被别文件import

package com.test.javademo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class MyClass1 {
}
public class MyClass {
      class Dog{
        public int age;
        public String name;
        public Dog(int age, String name) {
            super();
            this.age = age;
            this.name = name;
        }
        @Override
        public String toString() {
            return "Dog [age=" + age + ", name=" + name + "]";
        }
    }

    public static void main() {
        List<Dog> list= new ArrayList<>();
        list.add(new MyClass().new Dog(5, "DogA"));
        list.add(new MyClass().new Dog(6, "DogB"));
        list.add(new MyClass().new Dog(7, "DogC"));
        Collections.sort(list, new Comparator<Dog>() {
            @Override
            public int compare(Dog o1, Dog o2) {
                return o2.age - o1.age;
            }
        });

        System.out.println("---xfwang---给狗狗按照年龄倒序:"+list);
        Collections.sort(list, new Comparator<Dog>() {
            @Override
            public int compare(Dog o1, Dog o2) {
                return o1.name.compareTo(o2.name);
            }
        });
        System.out.println("---xfwang---给狗狗按名字字母顺序排序:"+list);
    }
}
执行结果:
 System.out: ---xfwang---给狗狗按照年龄倒序:[Dog [age=7, name=DogC], Dog [age=6, name=DogB], Dog [age=5, name=DogA]]
System.out: ---xfwang---给狗狗按名字字母顺序排序:[Dog [age=5, name=DogA], Dog [age=6, name=DogB], Dog [age=7, name=Do
gC]]

如何new :  list.add(new MyClass().new Dog(5, "DogA"));

4.2 泛型的简单例子

//创建一个泛型类
class MyClass <T>{
    private T obj;
    public MyClass(T obj) {
        this.obj = obj;
    }
    public T getData() {
        return obj;
    }
    public void setData(T obj) {
        this.obj = obj;
    }
}
//泛型的应用:
public class Demo1 {
    public static void main(String[] args) {
        MyClass<String> myClass1 = new MyClass<>("张三");   //注意这里的写法 new ,也是可以的。

        System.out.println(myClass1.getData());//这里不许需要强制类型转换
        MyClass<Integer> myClass2 = new MyClass<>(20);
        System.out.println(myClass2.getData());
    }
}

泛型还有很多变种, 就是多个参数,限定类型,泛型函数,等等

泛型函数:

package com.note.generic6;
import com.note.generic6.Animal;
import com.note.generic6.Cat;
import com.note.generic6.Dog;
public class Demo1 {
    public static void main(String[] args) {
        GenericClass genericClass = new GenericClass ();
        genericClass.println("abc");
        genericClass.println(12);
        genericClass.println(new Dog());
        genericClass.println(new Cat());
        GenericClass.print(12);
    }
}
class GenericClass  {
    //泛型方法
    public <T> void println(T t) {
        System.out.println(t);
    }
    //泛型方法重载
    public <T extends Animal> void println(T animal) {
        animal.eat();
    }
    //静态泛型方法
    public static <T> void print(T t) {
        System.out.println(t);
    }
}
interface Animal{
    public abstract void eat();
}
class Dog implements Animal{
    @Override
    public void eat() {
        System.out.println("啃骨头");
    }
}
class Cat implements Animal{
    @Override
    public void eat() {
        System.out.println("吃鱼肉");
    }
}

泛型三种:

  • ArrayList<T> al=new ArrayList<T>();指定集合元素只能是T类型
  • ArrayList<?> al=new ArrayList<?>();集合元素可以是任意类型,这种没有意义,一般是方法中,只是为了说明用法
  • ArrayList<? extends E> al=new ArrayList<? extends E>();
    泛型的限定:
    ----? extends E:接收E类型或者E的子类型。
    ----?super E:接收E类型或者E的父类型。

4.3使用集合转数组的方法
必须使用集合的 toArray(T[] array),传入的是类型完全
一样的数组,大小就是 list.size()。
例子:

List<String> list = new ArrayList<String>(2); 
list.add("first"); 
list.add("second"); 
String[] array = new String[list.size()];   // 这里定义数组
array = list.toArray(array);  //转换

5-强引用、弱引用、软引用、虚引用

强引用 > 软引用 > 弱引用 > 虚引用

5.1 强引用StrongReference
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。
Object o=new Object(); // 强引用
当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。如果不使用时,要通过如下方式来弱化引用,如下:o = null; 其实这样就违背了GC 原理,还需要
手动写o = null进行释放内存

情况一就是函数体内的强引用

public void test(){
    Object o=new Object();  //强引用
    // 省略其他操作
}

在一个方法的内部有一个强引用,这个引用保存在中,而真正的引用内容(Object)保存在中。当这个方法运行完成后就会退出方法栈,则引用内容的引用不存在,这个Object会被回收。

情况二 全局强引用
libcore/ojluni/src/main/java/java/util/ArrayList.java
ArrayList的实现源代码:
ArrayList class :

....
transient Object[] elementData;
....
public void clear() {
        modCount++;
        // Let gc do its work, gc 自动回收了elementData[i] 的object
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
}

在ArrayList类中定义了一个变量elementData数组,在调用方法清空数组时可以看到为每个数组内容赋值为null。 elementData[i] = null; 理解:
显式地设置elementData[i] 为null,超出对象的生命周期范围,则gc认为该对象不存在引用,这时就可以回收这个对象。具体什么时候收集这要取决于gc的算法,但是这个时候elementData != null.

对于第二种情况gc回收进行测试举例:
GCTarget.java

public class GCTarget {
    // 对象的ID
    public String id;

    // 占用内存空间
    byte[] buffer = new byte[1024];
    public GCTarget(String id) {
        this.id = id;
    }

    protected void finalize() throws Throwable {
        // 执行垃圾回收时打印显示对象ID
        Log.d("xfwang","Finalizing GCTarget, ----xfwang---id is : " + id);
    }

    public String getTargetId() {
        return id;
    }
}

测试线程:

    public static void creatThread(){
        GCTarget[] gcTarget = new GCTarget[2];
        new Thread(){
            @Override
            public void run() {
                super.run();
                Log.d("xfwang","creatThread=============xfwang===========================");
                gcTarget[0] = new GCTarget("0");
                gcTarget[1] = new GCTarget("1");
                Log.d(TAG, "color----xfwang--000-GCTarget ID ="+gcTarget[0].getTargetId());
                Log.d(TAG, "color----xfwang--111-GCTarget ID ="+gcTarget[1].getTargetId());
                // 通知GC进行垃圾回收
                gcTarget[0] = null;
                System.gc();
                try {
                    // 休息几分钟,等待上面的垃圾回收线程运行完成
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d(TAG, "color----xfwang--111-GCTarget=END TEST!");
            }
        }.start();
    }

测试结果:

2021-06-15 19:34:04.894 6791-6825/com.test.javademo D/xfwang: creatThread=============xfwang===========================
2021-06-15 19:34:04.894 6791-6825/com.test.javademo D/JavaUtils: color----xfwang--000-GCTarget ID =0
2021-06-15 19:34:04.894 6791-6825/com.test.javademo D/JavaUtils: color----xfwang--111-GCTarget ID =1
2021-06-15 19:34:06.473 6791-6806/com.test.javademo D/xfwang: Finalizing GCTarget, ----xfwang---id is : 0
2021-06-15 19:34:10.896 6791-6825/com.test.javademo D/JavaUtils: color----xfwang--111-GCTarget=END TEST!

从上面的结果可以看出 gcTarget[0] 的finalize 函数被执行到,资源被GC回收

如果数组的本质含义不理解, 可以从下图看出原理,实际数据在栈中,对象是在堆中,数据的gcTarget[0] 是对对象的引用。 如果没有了引用,GC 随时回收对象


5.2 弱引用
弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

    String str = new String("abc");
    WeakReference<String> weakReference = new WeakReference<>(str);
    str = null;  //这里相当于 强引用不存在
    System.gc();  //这里触发回收, 不实时
-------------------------------------------------------------------------------------------------
    String str = new String("abc");
    WeakReference<String> weakReference = new WeakReference<>(str);
    // 弱引用转强引用 , 这里对的,亲自测试
    String strongReference = weakReference.get();

弱引用应用原理:

A a = new A();
B b = new B(a);
a = null; 
System.gc  //到这里不进行a 的回收,因为B依然依赖与A,在这个时候,造成了内存泄漏!
---------------------------------------------------------------------------------
A a = new A();
B b = new B(a);
a = null;
b = null;
System.gc  //这里对a,b 进行了回收

使用弱引用对上面的进行改进:

A a = new A();
WeakReference wr = new WeakReference(a); 
a  = null;
System.gc  //这里a 对象被回收, 当 a=null ,这个时候A只被弱引用依赖,那么GC会立刻回收A这个对象,这就是弱引用的好处!他可以在你对对象结构和拓扑不是很清晰的情况下,帮助你合理的释放对象,造成不必要的内存泄漏!!

ReferenceQueue
在weak reference指向的对象被回收后, weak reference本身其实也就没有用了. java提供了一个ReferenceQueue来保存这些所指向的对象已经被回收的reference. 用法是在定义WeakReference的时候将一个ReferenceQueue的对象作为参数传入构造函数.
例子:

--------------------------------------GCTarget.java-----------------------------------
package com.test.javademo;
import android.util.Log;
public class GCTarget {
    // 对象的ID
    public String id;
    // 占用内存空间
    byte[] buffer = new byte[1024];
    public GCTarget(String id) {
        this.id = id;
    }
    protected void finalize() throws Throwable {
        // 执行垃圾回收时打印显示对象ID
        Log.d("xfwang","Finalizing GCTarget, ----xfwang---id is : " + id);
    }
    public String getTargetId() {
        return id;
    }
}
--------------------------------------GCTargetWeakReference---------------------------
package com.test.javademo;
import android.util.Log;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class GCTargetWeakReference extends WeakReference<GCTarget> {
    // 弱引用的ID
    public String id;
    public GCTargetWeakReference(GCTarget gcTarget,  ReferenceQueue<? super GCTarget> queue) {
        super(gcTarget, queue);
        this.id = gcTarget.id;
    }

    protected void finalize() {
        Log.d("xfwang","Finalizing ---xfwang---GCTargetWeakReference " + id);
    }
}
==========================测试线程=======================
    public static void creatThread(){
        ReferenceQueue<GCTarget> REFERENCE_QUEUE = new ReferenceQueue<>();
        new Thread(){
            public void run() {
                super.run();
                Log.d("xfwang","creatThread=============xfwang===========================");
                LinkedList<GCTargetWeakReference> gcTargetList = new LinkedList<>();

                // 创建弱引用的对象,依次加入链表中
                for (int i = 0; i < 5; i++) {
                    GCTarget gcTarget = new GCTarget(String.valueOf(i));
                    GCTargetWeakReference weakReference = new GCTargetWeakReference(gcTarget, REFERENCE_QUEUE);
                    gcTargetList.add(weakReference);
                    System.out.println("Just ---xfwang---created GCTargetWeakReference obj: "+gcTargetList.getLast());
                }

                // 通知GC进行垃圾回收
                System.gc();
                try {
                    // 休息几分钟,等待上面的垃圾回收线程运行完成
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 检查关联的引用队列是否为空
                Reference<? extends GCTarget> reference;
                while((reference = REFERENCE_QUEUE.poll()) != null) {
                    if(reference instanceof GCTargetWeakReference) {
                        System.out.println("In queue, id is: "+((GCTargetWeakReference) (reference)).id);
                    }
                }
            }
        }.start();
    }

==============测试打印结果============================

creatThread=============xfwang===========================
2021-06-16 09:38:02.231 15519-15553/com.test.javademo I/System.out: Just ---xfwang---created GCTargetWeakReference obj: com.test.javademo.GCTargetWeakReference@79c208d
2021-06-16 09:38:02.231 15519-15553/com.test.javademo I/System.out: Just ---xfwang---created GCTargetWeakReference obj: com.test.javademo.GCTargetWeakReference@9730242
2021-06-16 09:38:02.231 15519-15553/com.test.javademo I/System.out: Just ---xfwang---created GCTargetWeakReference obj: com.test.javademo.GCTargetWeakReference@1b44253
2021-06-16 09:38:02.231 15519-15553/com.test.javademo I/System.out: Just ---xfwang---created GCTargetWeakReference obj: com.test.javademo.GCTargetWeakReference@527b790
2021-06-16 09:38:02.231 15519-15553/com.test.javademo I/System.out: Just ---xfwang---created GCTargetWeakReference obj: com.test.javademo.GCTargetWeakReference@5373889
2021-06-16 09:38:03.937 15519-15534/com.test.javademo D/xfwang: Finalizing GCTarget, ----xfwang---id is : 4
2021-06-16 09:38:03.937 15519-15534/com.test.javademo D/xfwang: Finalizing GCTarget, ----xfwang---id is : 3
2021-06-16 09:38:03.937 15519-15534/com.test.javademo D/xfwang: Finalizing GCTarget, ----xfwang---id is : 2
2021-06-16 09:38:03.937 15519-15534/com.test.javademo D/xfwang: Finalizing GCTarget, ----xfwang---id is : 1
2021-06-16 09:38:03.937 15519-15534/com.test.javademo D/xfwang: Finalizing GCTarget, ----xfwang---id is : 0

5.3 软引用SoftReference
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用.

Browser prev = new Browser();     // 获取页面进行浏览
SoftReference<Browser > sr = new SoftReference<Browser >(prev); // 浏览完毕后置为软引用        
if(sr.get()!=null){ 
    rev = (Browser) sr.get();           // 还没有被回收器回收,直接获取
}else{
    prev = new Browser();               // 由于内存吃紧,所以对软引用的对象回收了
    sr = new SoftReference(prev);  // 重新构建
}

5.4 虚引用
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。

相关文章

网友评论

      本文标题:java 技术规范编程

      本文链接:https://www.haomeiwen.com/subject/ekhveltx.html