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