第二周周报

第二周周报

文章目录

  • 第二周周报
    • 1 本周总结
    • 2 思考题
        • 2.1 关于try/catch/finally代码块return的讨论
        • 2.2 枚举实现单例模式
        • 2.3 枚举缺省构造方法时的讨论
        • 2.4 集合相关
            • 2.4.1 Set中重复放入null对象
            • 2.4.2 HashMap的键值放入null
            • 2.4.3 ArrayList的默认长度及扩容机制
            • 2.4.4 TreeMap两种排序实现方式
            • 2.4.5 LinkedList和Queue的关系
        • 2.5 泛型相关
    • 3 下周计划

1 本周总结

  • 学习手机刷机基本操作,完成一次全量自检
  • 过完一遍《第一行代码》,完成demo
  • 开始深入学习Android知识,开始阅读《Android群英传》,并根据网上视频作为补充,同时进行适当的实战练习

2 思考题

2.1 关于try/catch/finally代码块return的讨论

下面验证当catch/finally代码块同时存在return语句时,返回哪个
TestException.java代码:

public class TestException extends Exception {
    public TestException() {
        System.out.println("Generate TestException");
    }
}

Test.java代码:

public class Test {
    public static void main(String[] args) {
        System.out.println(test());
    }

    private static String test() {
        try {
            generateException();
        } catch (TestException e) {
            System.out.println("Execute code in catch");
            return "Return code in catch";
        } finally {
            return "Return code in finally";
        }
    }

    private static void generateException() throws TestException {
        throw new TestException();
    }
}

运行结果:

Generate TestException
Execute code in catch
Return code in finally

可见,如果有finally代码块,即使catch代码块执行,也不会返回,最终将前面的return语句覆盖掉,返回finally中的语句

2.2 枚举实现单例模式

查阅资料了解到,单例模式的8种写法关键在于保证高并发环境下的线程安全,相比之下,通过枚举实现单例模式写法更简洁,实现简单,而且由于JVM的保证,不用担心线程安全问题。

public enum  SingletonEnum {
    INSTANCE;
    private String name;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
}

2.3 枚举缺省构造方法时的讨论

查阅Enum.class源码可知,当枚举类中无自定义构造方法时,将自动调用以下构造方法:

protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

当打印枚举中常量值时,会调用toString()方法打印枚举字符串

public String toString() {
        return this.name;
    }

2.4 集合相关

2.4.1 Set中重复放入null对象
Set<Integer> test = new HashSet<>();
System.out.println(test.add(null));
System.out.println(test.add(null));

输出结果:

true
false

表明Set中重复放入null时第一次添加成功,之后添加失败

2.4.2 HashMap的键值放入null
Map<String,Integer> map = new HashMap<>();
System.out.println(map.put("test",1));
System.out.println(map.put(null,3));
System.out.println(map.put(null,null));

输出结果:

null
null
3

可见,HashMap键值可放入null,put()方法返回值为null或者value值,若加入成功,则返回null,加入失败则返回value。这里想起HashSet的add方法,查阅源码发现

public boolean add(E e) {
        return this.map.put(e, PRESENT) == null;
    }

HastSet的add方法底层运用的也是HashMap的put方法,返回值与null比较返回boolean值

2.4.3 ArrayList的默认长度及扩容机制

1.初始化长度
查阅ArrayList.class源码中的构造方法一共有三种

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
transient Object[] elementData;
//构造方法1
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else {
            if (initialCapacity != 0) {
                throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
            }

            this.elementData = EMPTY_ELEMENTDATA;
        }

    }
//构造方法2
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
//构造方法3
  public ArrayList(Collection<? extends E> c) {
        this.elementData = c.toArray();
        if ((this.size = this.elementData.length) != 0) {
            if (this.elementData.getClass() != Object[].class) {
                this.elementData = Arrays.copyOf(this.elementData, this.size, Object[].class);
            }
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }

    }
  • 给出初始化容量:判断初始化容量是否合法(是否大于等于0),若大于0则初始化一个指定长度数组,等于零初始化一个长度为0数组,小于0抛出异常
  • 无参初始化:初始化一个长度为0的数组
  • 集合参数初始化:大小为参数转换成数组的长度

2.扩容机制
源码中扩容主要函数如下:

private int newCapacity(int minCapacity) {
        int oldCapacity = this.elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(10, minCapacity);
            } else if (minCapacity < 0) {
                throw new OutOfMemoryError();
            } else {
                return minCapacity;
            }
        } else {
            return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
        }
    }

新容量newCapacity为原有容量增加二分之一,然后判断新容量和所需最小容量大小,若此时新容量依然小于所需最小容量,分别判断:

  • elementData是否为空,为空则返回所需最小容量与10的最大值
  • 所需最小容量是否合法(大于0),不合法则抛出异常
  • 满足条件则直接将最小容量作为新容量
  • 最后验证新容量是否超过最大容量,超过则将最大容量作为扩容容量
2.4.4 TreeMap两种排序实现方式

TreeMap默认按key的自然序排列,以下分别实现按key排序与value并输出结果。按value排序大致思路为将TreeMap转换成List后通过比较器进行排序

public class Main {
    public static void main(String[] args) {
        TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();

        treeMap.put("c", 2);
        treeMap.put("d", 1);
        treeMap.put("a", 4);
        treeMap.put("b", 3);

        System.out.println("按key排序:");
        for (Map.Entry<String, Integer> entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }

        Iterator<Map.Entry<String, Integer>> it = treeMap.entrySet().iterator();
        List<Map.Entry<String, Integer>> list = new ArrayList<>();

        while (it.hasNext()) {
            list.add(it.next());
        }

        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
                return e1.getValue() - e2.getValue();
            }
        });

        System.out.println("按value排序:");
        for (Map.Entry<String, Integer> entry : list) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }
    }
}

输出结果:

按key排序:
a 4
b 3
c 2
d 1
按value排序:
d 1
c 2
b 3
a 4
2.4.5 LinkedList和Queue的关系

LinkedList源码中有如下声明:

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable

可见LinkedList实现了Deque接口,而继续深入发现:

public interface Deque<E> extends Queue<E>

说明LinkedList的一个实现

2.5 泛型相关

  • 泛型标识符的含义
    查阅《JAVA核心技术 卷Ⅰ》

类型变量使用大写形式,且比较短。在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型,T表示“任意类型”,需要时还可以用临近的字母U和S表示。

  • 泛型擦除的影响

类型擦除会导致很多特性无法使用,解决办法是通过反射,绕过编译器对泛型的限制

  • instanceof的使用
List<String> list = new ArrayList<>();
list.add("test");
System.out.println(list.get(0).getClass() instanceof String);

最后一行报错

Inconvertible types;cannot cast 'java.lang.Class>' to 'java.lang.String'
  • 泛型能否在异常中使用
public class MyException<T> extends Throwable {}

报错信息:

Generic class may not extend 'java.lang.Throwable'

以下是查阅到的原因:来自stackoverflow

Both SomeException and SomeException are erased to the same type, there is no way for the JVM to distinguish the exception instances, and therefore no way to tell which catch block should be executed.

3 下周计划

  • 网上找了一套视频,下周继续阅读《Android群英传》、《Android开发艺术探索》,并根据视频敲代码
  • 着手开始做课题研究“java数据结构与集合”
  • 随时想到想做的练手需求随时实现

你可能感兴趣的:(第二周周报)