【学习笔记】《Java编程思想》 第8~11章

时间:2019-12-23
本文章向大家介绍【学习笔记】《Java编程思想》 第8~11章,主要包括【学习笔记】《Java编程思想》 第8~11章使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

第八章 多态

  1. 多态的条件:

    1. 要有继承

    2.父类对象引用子类对象

    3. 要有方法的重写

  2. 多态的作用:消除类型之间的耦合关系。
  3. 将一个方法调用与一个方法主体关联起来称作绑定。若在程序执行前进行绑定,叫做前期绑定;在运行时根据对象的类型进行绑定,叫做后期绑定,也叫动态绑定、运行时绑定。
  4. Java中除了static方法和final方法之外,其他所有的方法都是后期绑定。所以final的作用还有关闭动态绑定。
  5. 只有非private方法才可以被覆盖,但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们的预期的执行。
  6. 如果某个子对象要依赖于其他对象,销毁的顺序应该和初始化顺序相反。
  7. 域是不具有多态性的,只有普通的方法调用是多态的。如果直接访问某个域,这个访问就将在编译期进行解析,即域是静态解析的。静态方法也是不具有多态性的

第九章 接口

  1. 抽象方法和抽象类:

    抽象方法是不完整的,仅有声明而没有方法体
    abstract void f();

    包含抽象方法的类叫做抽象类;如果一个类包含一个或多个抽象方法,该类必须被定义为抽象的
    如果从一个抽象类继承,并想创建该类的对象,那么就必须为基类中的所有抽象方法提供方法定义;如果不这样做,那么导出类也是抽象类,且编译器会强制我们用abstract关键字来限制这个类。
  2. interface这个关键字产生一个完全抽象的类,它根本就没有提供任何具体实现

    要让一个类遵循某个特定接口(或一组接口),需要使用implements关键字

  3. 如果从一个非接口的类继承,那么只能从一个类继承;可以继承多个接口
  4. 接口可以多继承接口来扩展,接口还可以嵌套。
  5. 接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法。在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。
  6. 接口可以包含域,且隐式地是static 和 final的。
  7. 接口关键字interface前可以添加public修饰符,不加默认是包访问权限,接口的方法默认都是public的。

第十章 内部类

  1. 将一个类的定义放在另一个类的定义内部,这就是内部类。
  2. 从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须具体地指明这个对象的类型OuterClassName.InnerClassName。
  3. 非static的普通内部类自动拥有对其外围类所有成员的访问权(包括private)

  4. 如果需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟.this
    public class DotThis {
        void f() {
    	System.out.println("DotThis.f()");
        }
        public class Inner {
    	public DotThis outer() {
    	    return DotThis.this;//通过.this返回外部类对象
    	}
        }
        public Inner inner() {return new Inner();}
        public static void main(String[] args) {
    	DotThis dotThis = new DotThis();
    	DotThis.Inner dtInner = dotThis.inner();
    	dtInner.outer().f();
        }
    }
    

    如果要创建内部类对象,必须使用外部类对象和.new

    public class DotNew {
        public class Inner {}
        public static void main(String[] args) {
            DotNew dn = new DotNew();
            DotNew.Inner dni = dn.new Inner();
        }
    }
    
  5. 如果拥有的是抽象类或者具体类,而不是接口,那么就只能使用内部类实现多重继承
    public class Example1 {
        public String getName() {
            return "llb";
        }
    }
    public class Example2 {
        public int getAge() {
            return 25;
        }
    }
    
    public class MultiImplementation {
        private class test1 extends Example1 {
            public String getName() {
                return super.getName();
            }
        }
        private class test2 extends Example2 {
            public int getAge() {
                return super.getAge();
            }
        }
        public String getName() {
            return new test1.getName();
        }
        public int getAge() {
            return new test2.getAge();
        }
    
        public static void main(String[] args) {
            MultiImplementation my = new MultiImplementation();
            System.out.println("姓名: " + my.getName());
            System.out.println("年龄: " + my.getAge());
        }
    }
    
  6. 局部内部类是指内部类定义在方法或作用于内

      • 局部内部类不能有访问说明符
      • 局部内部类可以访问当前代码块内的常量以及此外围类的所有成员

     局部内部类会跟着其他类一起通过编译,但是在定义该局部内部类的方法或作用域之外,该局部内部类是不可用的

  7. 内部类声明为static时,不再包含外围对象的引用.this,称为嵌套类(与C++嵌套类大致相似,只不过在C++中那些类不能访问私有成员,而在Java中可以访问)。
    - 创建嵌套类,不需要外围对象。
    - 不能从嵌套类的对象中访问非静态的外围对象。
    public class OuterClass {
        private static String address = "Shanghai";
        public static class StaticInnerClass {
            public String getAddress() {
    	    return address;
    	}
        }
        public static void main(String[] args) {
    	OuterClass.StaticInnerClass sic = new  
                                         OuterClass.StaticInnerClass();
    	String address = sic.getAddress();
    	System.out.println(address);
        }
    }
    
  8. 匿名内部类创建方式为:

    new 外部类构造器(参数列表)或接口 {}
    1. 在定义类的同时就生成了该内部类的一个实例,随后该类的定义会消失,所以匿名内部类不能被重复使用
    2. 匿名内部类必须继承一个父类或者实现一个接口,但是也只能继承一个父类或实现一个接口
    3. 匿名内部类中不能定义构造方法(没有类名),不能存在任何的静态成员变量和静态方法
    4. 匿名内部类不能是抽象的,它必须实现所继承的类或者实现的接口中的全部抽象方法
    public interface Contents {
        int value();
    }
    public class Parcel7 {
        public Contents contents() {
            return new Contents() {
                private int i = 1;
                public int value() { return i; }
            };
        }
    
        //等价于
    /*
        class MyContents implements Contents {
            private int i = 1;
            public int value() { return i; }
        }
        public Contents contents() { return new MyContents(); }
    */
    
        public static void main(String[] args) {
            Parcel7 parcel7 = new Parcel7();
            Contents c = parcel7.contents();
        }
    }

    给匿名内部类传递参数时,若该形参在内部类被使用,那么该形参必须被声明为final

    public class Parcel9 {
        //dest是一个在外部定义的对象,必须将其定义为final参数引用
        public Destination destination(final String dest) {
    	return new Destination() {
    	    private String label = dest;
    	    @Override
    	    public String readLabel() {
                    return label;
    	    }
    	};
        }
        public static void main(String[] args) {
    	Parcel9 parcel9 = new Parcel9();
    	Destination destination = parcel9.destination("Shanghai");
    	System.out.println(destination.readLabel());
        }
    }
  9. 为什么要是final?内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的参数进行备份,自己内部方法调用的实际上是自己的属性而不是外部方法传递进来的参数,在内部类中的属性和外部方法的参数两者看似是同一个东西,但实际上却不是,也就是说在内部类中对属性的修改并不会影响到外部的形参,如果内部类中的属性改变了,而外部方法的形参却没有改变,这是难以接受的,为了保证参数的一致性,就规定使用final来避免两者不同时变化的情况发生。
  10. 每个类都会产生一个.class文件,其中包含了如何创建该类型的对象的全部信息;内部类也必须生成有个.class文件以包含它们的class对象信息,其命名规则是:

    外围类的名字,加上”$“,再加上内部类的名字,如果时匿名内部类,编译器会简单地产生一个数字作为其标识符,例如:

    Outer$Inner.class
    Outer$1.class

第十一章 持有对象

  1. Collection是一个集合接口,提供了对集合对象进行操作的通用接口方法
    Collections是一个包装类,包含有各种集合操作的静态方法,此类不能实例化,就像一个工具类,服务于Collection框架
    Collection接口是最基本的集合接口,一个Collection代表一组Object,即Collection的元素
  2. List,Set,Queue接口都是Collection接口的实现

    • List:必须按照插入的顺序保存元素
    • Set:不能有重复的元素
    • Queue:按照队列的规则来确定对象产生的顺序(通常与元素插入的顺序相同)
  3. 在Java.util包中的Arrays和Collections类中都有很多实用方法,可以在Collection中添加一组元素;Arrays.asList()方法接受一个数组或是一个用逗号分割的元素列表(使用可变参数),并将其转换为一个List对象;Collections.addAll()方法接受一个Collection对象,以及一个数组或是一个用逗号分割的列表,将元素添加到Collection中
    import java.util.*;
    
    public class AddingGroups {
        public static void main(String[] args) {
    	Collection<Integer> collection = 
    	    new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));
    	Integer[] moreInts = {6, 7, 8, 9, 10};
    	collection.addAll(Arrays.asList(moreInts));//更快,但不够灵活
    	Collections.addAll(collection, 11, 12, 13, 14, 15);
    	Collections.addAll(collection, moreInts);//更加灵活
    	List<Integer> list = Arrays.asList(16, 17, 18, 19, 20);
    	list.set(1, 99);
        }
    }
  4. Arrays类似于Collections,是一个工具类

    Arrays.asList()返回一个受指定数组支持的固定大小的列表,可以用来将数组转换成List

    反过来,利用List的toArray()方法,可以将List转换成数组

  5. 容器的打印

    必须使用Arrays.toString()来产生数组的可打印表示

  6. List

    ArrayList:随机访问,但是在List的中间插入或移除元素时较慢

    LinkedList:通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问;随机访问较慢

    ArrayList常见方法

    contains(Object o):确定某个对象是否在列表中

    remove(int index):移除指定位置上的元素

    indexOf(Object o):返回列表中首次出现指定元素的索引,如果不包含该元素,返回-1

    add(E e):将制定元素添加到此列表的尾部

    add(int index, E e):将指定元素插入到指定位置

  7. 迭代器是一个对象,它的工作是遍历并选择序列中的对象

    Java中的Iterator只能单向移动,只能用来:

    1. 使用方法iterator()要求容器返回一个Iterator;Iterator准备好返回序列的第一个元素
    2. 使用next()获得序列中的下一个元素
    3. 使用hasNext()检查序列中是否还有元素
    4. 使用remove()将迭代器新近返回的元素删除
  8. LinkedList常见方法

    addFirst(E e)/addLast(E e):将元素添加到列表的开头/结尾

    getFirst()/element():返回列表的第一个元素

    peek()/peekFirst():获取但不移除列表的第一个元素

    offer(E e)/offerLast(E e):将元素插入到列表末尾

  9. Queue

    队列时一个典型的先进先出(FIFO)的容器,即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是一样的

    LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现,也可以将LinkedList向上转型为Queue

  10. Set

    Set不保存重复的元素;Set最常被使用的是测试归属性,我们可以很容易地询问某个对象是否在某个Set中

    存储元素的方式:

    HashSet:使用散列函数

    LinkedHashSet:使用散列,但是看起来使用了链表来维护元素的插入顺序

    TreeSet:将元素存储在红-黑树结构中

  11. Map:一组成对的“键值对”对象,允许使用键来查找值;映射表允许我们使用另一个对象来查找某个对象,它被称为“关联数组”,因为它将某些对象与另外一些对象关联在了一起,或者被称为“字典”

    Map<Integer, Integer> map = new HashMap<Integer, Integer>();

    更复杂的形式

    Map<Integer, List<String>> map = 
                                   new HashMap<Integer, List<String>>();
    map.put(1, rrays.asList("lv", "long", "bao"));

    map的键是一个Set,值是一个Collection

    Map常见方法

    get(Object o):返回指定键所映射的值,如果不包含该键的映射关系,返回null

    put(K key, V value):将指定的值与此映射中的指定键关联,如果已经存在映射关系,更新值

    hashCode():返回此映射的哈希码值

    Map的三种实现

    HashMap:基于“拉链法”实现的散列表,一般用于单线程中,不是线程安全的

    HashTable:基于“拉链法”实现的散列表,一般用于多线程中,是线程安全的

    TreeMap:有序的散列表,通过红黑树实现的,一般用于单线程中存储有序的映射

  12. 总结:
    1. 数组将数字与对象联系起来:它保存类型明确的对象,查询对象时,不需要对结果做类型转换;它可以时多维的,可以保存基本类型的数据;但是,数组一旦生成,其容量不能改变
    2. Collection保存单一的元素,而Map保存关联的键值对:有了Java泛型,你就可以指定容器中存放的对象类型,因此你就不会将错误类型的对象放置到容器中,并且在从容器中获取元素时,不必进行类型转换;各种Collection和Map都可以在你向其中添加更多的元素时,自动调整其尺寸;容器不能持有基本类型,但是自动包装机制会仔细地执行基本类型到容器中所持有包装器类型之间的双向装换
    3. 像数组一样,List也建立数字索引与对象的关联,因此,数组和List都是排好序的容器;List能够自动扩充容量
    4. 如果要进行大量的随机访问,就使用ArrayList;如果要经常从表中间插入或删除元素,则应该使用LinkedList
    5. 各种Queue以及栈的行为,由LinkedList提供支持
    6. Map是一种将对象(而非数字)与对象相关联的设计:HashMap设计用来快速访问;TreeMap保持“键”始终处于排序状态,所以没有HashMap快;LinkedHashMap保持元素插入的顺序,但是也通过散列提供了快速访问能力
    7. Set不接受重复元素:HashSet提供了最快的查询速度;TreeSet保持元素处于排序状态;LinkedHashSet以插入顺序保存元素
    8. 新程序中不应该使用过时的Vector,Hashtable和Stack(原因参考:https://www.cnblogs.com/fudashi/p/7214609.html

原文地址:https://www.cnblogs.com/mcq1999/p/12083193.html