第一次读Think In Java收获

        新进公司的初级程序员,看了公司的项目,目前没啥事做就在看Think In Java,然后想把自己的收获记录起来。这是本人第一次写博客。

 2018年5月25日

        1.创建一个新对象之后,直接输出该对象,会默认调用Object(所有类都默认继承Object类)的toString方法,也就是输出了该对象的地址(“@”符号加上一堆没有意义的数字和字母),如果该对象重写了toString方法,则会打印出重写方法中定义的内容。

例子:

(1)没有重写toString()时:

public class User {

}

public class test {
    public static void main(String[] args) {
        User user = new User();
        System.out.println(user);
    }
}

  输出结果为:com.eno.springbootweb.domain.User@7291c18f

(2)重写了toString()方法

    

public class User {
    @Override
    public String toString() {
        return "123";
    }
}
public class test {
    public static void main(String[] args) {
        User user = new User();
        System.out.println(user);
    }
}
输出结果:123

        2.多态的缺陷,“覆盖”私有方法

例子:

class A{
    private void f(){
        System.out.println("a");
    }
    public static void main(String[] args) {
        A a = new Test();
        a.f();
    }
}

public class Test extends A{
    public void f(){
        System.out.println("test");
    }
}

输出结果为:a

        我们期待的是输出test,但是由于private修饰符修饰的方法是被自动认为是final的方法,而且对子类是隐蔽的。因此,这种情况下,子类中的f()方法就是一个全新的方法;既然父类中的方法f()方法在子类中不可见,因此甚至也不能被重载。

        结论:只有非private方法才可以被覆盖;但是还需要密切注意覆盖private方法的现象,这是虽然编译器不会报错,但是也不会按照我们所期望的来执行。所以在子类中,对于父类中的private的方法最好采用不同的名字。


3.使用继承和组合的场景

        在使用继承的时候只要考虑是否需要向上转型,如果需要则使用继承,不需要使用组合效率更高。


4.代理类

代码如下:

class A{
    public String a(){
        return "a";
    }
    public String b(){
        return "b";
    }
}

class Agent{
    private A a = new A();
    public void getA(){
        System.out.println(a.a());
    }
    public void getB(){
        System.out.println(a.b());
    }
}

public class Test{
    public static void main(String[] args) {
        Agent agent = new Agent();
        agent.getA();
        agent.getB();
    }
}


5.组合、继承以及多态在构建顺序上的作用(构造器、成员变量加载的顺序)

例子:

class A{
    A(){
        System.out.println("A");
    }
}

class B{
    B(){
        System.out.println("B");
    }
}

class C{
    C(){
        System.out.println("C");
    }
}

class C1 extends C{
    C1(){
        System.out.println("C1");
    }
}

public class Test extends C1{
    private A a = new A();
    private B b = new B();
    public Test(){
         System.out.println("test");
     }
    public static void main(String[] args) {
        new Test();
    }
}

结果:

C
C1
A
B

test

结论:

        构建顺序首先是找到该对象的最上层的基类,然后是下一级子类直到加载到当前类,加载到本类的对象时,会先按成员声明的顺序加载成员变量,最后在调用该类的构造器(同一类中构造器会在成员变量初始化之后被调用)。


6.构造器内部的多态方法的行为

class A{
   void a(){
        System.out.println("A.a()");
    }
    A(){
        System.out.println("A() before a()");
        a();
        System.out.println("A() after a()");
    }
}

class B extends A{
    private int radius = 1;
    B(int r){
        radius = r;
        System.out.println("B.B().redius = " + radius);
    }
    void a() {
        System.out.println("B.a().radius = " + radius);
    }
}

public class Test{
    public static void main(String[] args) {
        new B(5);
    }
}

结果:

        A() before a()
        B.a().radius = 0
        A() after a()
        B.B().redius = 5

结论:

        从结果可以看出,在基类的构造器中调用了被子类重写的方法,会执行子类中重写的方法,而不是执行基类中的方法。而在调用到子类中重写的方法的时候,子类(B)还没有被构造,也就导致了radius并没有被初始化(输出结果为0)。因此,编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法”。


7、Collenction集合

ArrayList和LinkedList都是List类型,他们都按照插入的顺序保存元素。两者的区别主要是在执行某些类型的操作时的性能,ArrayList遍历只花费常量时间,LinkedList在插入删除元素是花费常量时间。

HashSet、TreeSet和LinkedHashSet都是Set类型,Set中的元素不可重复,HashSet(使用了散列)是快速获取元素的方法,因此不考虑存储顺序,TreeSet(使用红-黑树)会按照比较结果升序保存对象, LinkedHashSet会按添加的顺序保存对象。

Map不用考虑尺寸问题,因为它自己会自动地调整尺寸。HashMap和HashSet一样提供了最快的查找技术,也没有按照任何明显的顺序进行保存元素,TreeMap会按照比较结果升序保存对象,LinkedHashMap会按添加的顺序保存键,同时保留了HashMap的查询速度。


8、List list = list1.subList(index1, index2)方法从一个List中截取一部分内容,然后将这个结果赋给另一个list之后,使用list1.containsAll(list)会显示true,就算我们使用了Collection.sort()和Collection.shuffle()之后,再调用list1.containsAll(list)仍返回true,由此看出顺序并不重要,因为subList()所产生的列表的幕后是初始列表,因此,对所返回的列表的修改都会反映到初始列表中。测试代码如下:

public class Test{
    public static void main(String[] args) {
        List list1 = new ArrayList();
        list1.add(1);
        list1.add(3);
        list1.add(2);
        list1.add(9);
        list1.add(4);

        System.out.println("list1" + list1.toString());
        List list2 = list1.subList(0,3);
        Collections.sort(list2);
        System.out.println("sort list2" + list2.toString());
        System.out.println("list1" + list1.toString());
        System.out.println(list1.containsAll(list2));
        Collections.shuffle(list2);
        System.out.println("shuffle list2" + list2.toString());
        System.out.println(list1.toString());
        System.out.println("list1" + list1.containsAll(list2));
    }
}

结果为:


还有几个个常用方法,retainAll()取交集,removeAll()移除所有元素,需要注意的是这两个方法都是基于equals()方法的。

set(index, obj),替换指定位置的元素。addAll()可以追加元素到列表中,也可以从指定位置追加,isEmpty()用来判断是否为空,clear()清除列表中的内容,toArray()将集合转换为数组类型等。


9、我们都知道queue队列是先进先出的模式(通过等待时长来判断是否为先进的元素),但有些时候需要在队列中基于优先级处理对象,于是就出现了PriorityQueue。

        PriorityQueue类在Java1.5中引入并作为 Java Collections Framework 的一部分。PriorityQueue是基于优先堆的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的时排序。

        优先队列不允许空值,而且不支持non-comparable(不可比较)的对象,比如用户自定义的类。优先队列要求使用Java Comparable和Comparator接口给对象排序,并且在排序时会按照优先级处理其中的元素。
        优先队列的头是基于自然排序或者Comparator排序的最小元素。如果有多个对象拥有同样的排序,那么就可能随机地取其中任意一个。当我们获取队列时,返回队列的头对象。
        优先队列的大小是不受限制的,但在创建时可以指定初始大小。当我们向优先队列增加元素的时候,队列大小会自动增加。

PriorityQueue是非线程安全的,所以Java提供了PriorityBlockingQueue(实现BlockingQueue接口)用于Java多线程环境。



还没看多少,持续更新中。。。。。。)

猜你喜欢

转载自blog.csdn.net/qq_32285039/article/details/80449160
今日推荐