OopStorm

java核心技术卷-基础知识

2019/05/30 Share

java核心技术卷-基础知识 | 8.0

  • 类之间的关系:
  • 依赖: (user-a)
    一个类的方法操纵另一个类的对象。
    
  • 聚合: (has-a)[关联]
    类A的对象包含类B的对象
    
  • 继承: (is-a)

    类A扩展类B,类A不但包含从类B继承的方法,还会拥有一些额外的功能
    
  • 对象

    要想使用对象,就必须首先构造对象,并指定其初始状态。
    
  • 构造器
    构造器与类同名
    每个类可以有一个以上的构造器
    构造器可以有多个参数
    构造器没有返回值
    构造器伴随new操作一起调用
    

-隐式参数与显式参数

1
2
3
4
public void reaiseSalary(double byPercent) {
double raise = this.salary * byPercent /100
this.salary += raise
}

  • final实例域

    构建时必须初始化这样的域,之后不能被修改
    
  • 静态域
    如果将域定义为static,每个类只有一个这样的域。而每一个对象对于所有的实例域都有自己的一份拷贝。

    1
    private static int nextId = 1;
  • 静态常量

    1
    public static final double PI = 3.1415926
  • 静态方法

    是一种不能向对象实施操作的方法。例如Math类的pow方法
    Math.pow(x, a)   运算时,不使用任何Math对象,换句话说,没有隐式参数
    静态方法可以访问自身类中的静态域 
    
    1
    2
    3
    public static int getNextId() {
    return nextId;
    }
  • 方法

    所有的方法定义在类内
    用于操作对象以及存取他们的实例域
    
  • 方法参数

    按值调用,方法得到的是所有参数值的一个拷贝
    一个方法不能修改一个基本数据类型的对象
    一个方法可以改变一个对象参数的状态
    一个方法不能让对象参数引用一个新的对象
    
  • 初始化数据域方法

    1.在构造器中设置值
    2.在声明中赋值
    3.初始化块
    
  • 参数名

    参数变量用同样的名字将实例域屏蔽起来,采用this.的形式访问实例域。this指示隐式参数,也就是所构造的对象。
    
  • 对象构造

    重载
            多个方法有相同的名字,不同的参数,变产生了重载
    
  • 对象析构与finalize方法

    析构器最常见的操作是回收分配给对象的存储空间,由于java有自动的垃圾回收器,不需要人工回收内存。
    如果某些对象使用了内存之外的其他资源,当资源不需要时,将其回收十分重要
    可以为任何一个类添加finalize方法,finalize方法将在垃圾回收器清楚对象之前调用。
    
  • Java允许使用包将类组织起来,借助于包可以方便地组织自己的代码,并将自己的代码与别人提供的代码库分开管理
    
  • 类的导入

    一个类可以使用所属包中的所有类,以及其他包中的公有类
    import  java.util.*; 加载java.util中的所有类
    
    1
    2
    Import java.time.LocalDate;
    Ex: localDate today = localDate.now();
Java.util和java.sql包都有Date类,如果同时使用,在每个类前面加上完整的包名
1
java.util.Date deadline = new java.util.Date();
  • 静态导入

    import语句不仅可以导入类,还增加了导入静态方法和静态域的功能
    
    1
    Import static java.lang.System.*
    就可以使用System类的静态方法和静态域,而不必加类名前缀
  • 将类放入包中

    要想将一个类放入包中,就必须将包的名字放在源文件的开头,包中定义类的代码之前。
    
    1
    Package com.horstmann.corejava
    如果没有在源文件中放置package语句,这个源文件中的类就被放置在一个默认包中 将包中的文件放到与完整的包名匹配的子目录中(com/horstmann/corejava)
  • 包的作用域

    标记为private的部分只能被定义它们的类使用
    标记为public的部分可以被任意的类使用
    如果没有指定public或private,这个部分可以被同一个包中的所有方法访问
    
  • 类路径

    类的路径必须与包名匹配
    为了使类能够被多个程序共享,需要做到下面几点
    1.把类放到一个目录中,例如/home/user/classdir。需要注意,这个目录是包树状结构的基目录。如果希望将com.horstmann.corejava.Employee类添加其中,这个Employee.class类文件就必须位于子目录/home/user/classdir/com/horstmann/corejava中
    2.将JAR文件放在一个目录中,例如: /home/user/archives。
    3.设置类路径。类路径是所有包含类文件的路径的集合
     c : \ classdir ; . ; c : \ archives \ archive . j ar
    
  • 文档注释

    javadoc 实用程序 ( utility ) 从下面几个特性中抽取信息 :
    ? 包
    ? 公有类与接口
    ? 公有的和受保护的构造器及方法
    ?公有的和受保护的域
    
  • 类注释

    类注释必须放在 import 语句之后 , 类定义之前 。
    
  • 方法注释

    每一个方法注释必须放在所描述的方法之前
    除了通用标记之外 , 还可以使用下面的标记 :
    ? @ param 变量描述
    这个标记将对当前方法的 “ param ” ( 参数 ) 部分添加一个条目 。 这个描述可以占据多行 , 并可以使用 HTML 标记 一个方法的所有@param 标记必须放在一起
    ? @ return 描述
    这个标记将对当前方法添加 “ return ” ( 返回 ) 部分,这个描述可以跨越多行 , 并可以使用 HTML 标记
    ? ? throws 类描述
    这个标记将添加一个注释 , 用于表示这个方法有可能抛出异常 。 
    
  • 还有域注释(静态常量),通用注释

  • 类设计的技巧

    1.一定要保证数据私有(不要破坏封装性)
    2.一定要对数据初始化(不要依赖于系统的默认值,应该显式的初始化)
    3.不要在类中使用过多的基本类型(用其他的类代替多个相关的基本类型的使用)
    4.不是所有的域都需要独立的域访问器和域更改器
    5.将职责过多的类进行分解
    6.类名和方法名要能够体现他们的职责(形容词修饰名词)
    7.优先使用不可变得类
    
  • 继承

    - 定义子类
            下面是由继承 Employee 类来定义 Manager 类的格式 , 关键字 extends 表示继承
            public class Manager extends Employee
            已存在的类为超类,父类,新类为子类 
            设计类时通用的方法放在超类,特殊用途的方法放在子类
    
    - 覆盖方法
            超类中的有些方法对子类不一定适用,需要提供一个方法来覆盖超类中的这个方法
            public double getSalary() {
                double baseSalary = super.getSalary();
                return baseSalary + bonus;
             }
    
    - 子类构造器
            public Manager ( String name , double salary , int year , int month , int day )
            {
                super ( name , salary , year , month , day ) ;
                bonus = 0 ;
             }
            由于 Manager 类的构造器不能访问 Employee 类的私有域 , 所以必须利用Employee 类的构造器对这部分私有域进行初始化,我们可以通过 super 实现对超类构造器的调用使用super 调用构造器的语句必须是子类构造器的第一条语句 。
    
    - 继承层次
             由一个公共超类派生出来的所有类的集合被称为继承层次
    
    - 多态
            对象变量是多态的,既可以引用一个超类对象,也可以引用超类的任何一个子类的对象
            
    1
    2
    3
    Manager boss = new Manager(...);
    Emplyee[] staff = new Employee[3];
    staff[0] = boss;
    例子中,变量staff[0]与boss引用同一个对象,但编译器将staff[0]看成Employee对象
    1
    2
    boss.setBouns(500)  Ok
    staff[0].setBouns(500); Error
    这是因为staff[0]声明类型是Employee 不可以将超类的引用赋给子类 - 多态的实现 java实现多态有三个必要条件: 继承,重写,向上转型
    1
    Wine a = new JNC();
    这里定义了一个Wine 类型的a,它指向JNC对象实例。由于JNC是继承与Wine,所以JNC可以自动向上转型为Wine,所以a是可以指向JNC实例对象的 (理解方法调用) - 阻止继承: final类和方法 不允许扩展的类被称为final类,final类的所有方法自动称为final方法,方法被声明为final,子类就不能覆盖这个方法 - 强制类型转换
    1
    2
    double x = 3.405;
    int nx = (int) x;
    将一个超类的引用赋给一个子类变量,必须进行类型转换 进行类型转换的原因,在暂时忽视对象的实际类型之后,使用对象的全部功能 只能在继承层次内进行类型转换 在将超类转换成子类之前 , 应该使用instanceof 进行检查
    1
    2
    3
    if (staff[1] instanceof Manager) {
    boss = (Manager) staff[1]
    }
    instanceof 可以在继承关系中用来判断一个实例是否属于它的父类型 - 抽象类 位于上层的类更具通用性,更加抽象。祖先类作为派生其他类的基类,而不作为使用的特定实例类。例如雇员是一个人,学生是一个人,person为超类,他们为子类,每个人都有姓名属性,所以将getName放入更高层次的类中,增加一个getDescription方法,返回一个人的描述,在子类中实现很容易,但是在超类Person中不知道提供什么内容,只是知道名字。使用abstract关键字,就不需要实现这个方法。 包含一个或多个抽象方法的类本身必须声明为抽象类
    1
    2
    3
    public abstract class Person {
    public abstract String getDescription();
    }
    除了抽象方法之外,抽象类还可以包含具体数据和具体方法 抽象方法充当占位的角色,具体实现在子类中。扩展抽象类可以有两种选择。一种在抽象类中定义部分抽象类方法或不定义抽象类方法,这样就必须将子类也标记为抽象类。另一种是定义全部的抽象方法,这样子类就不是抽象类了。 抽象类不能被实例化 - 受保护访问 最好类中的域标记为private,而方法标记为public,声明为private,子类也不能访问超类的私有域,当希望超类中的某些方法被子类访问,需要将方法或域声明为protected
  • Object: 所有类的超类

    除了基本类型不是对象。
    
    - equals方法
            用来检测一个对象是否等于另外一个对象。这个方法判断两个对象是否具有相同的引用。对于多数类来说这种判断并没有什么意义(在实际的雇佣数据库中,比较ID更有意义)
    
    - hashCode方法
            散列码是由对象导出的一个整型值
            每个对象都有一个默认的散列码,其值为对象的存储地址
    
    - toString方法
            用于返回表示对象值得字符串
            只要对象与一个字符串通过操作符+连接起来,java编译会自动地调用toString方法,以便获得这个对象的字符串描述
    
  • 泛型数组列表

    - ArrayList使用起来有点像数组,但在添加或删除元素时,具有自动调节数组容量的功能,而不需要为此编写任何代码。
    - ArrayList是一个采用类型参数的泛型类。为了制定数组列表保存的元素对象类型,需要用一对尖括号将类名括起来加在后面。
            
    1
    2
    ArrayList<Employee> staff = new ArrayList<Employee>();
    ArrayList<Employee> staff = new ArrayList<>();
    使用add方法将元素添加到数组列表中
    1
    2
    staff.add(new Employee("hary",...));
    staff.add(n, e); // 在数组列表中间插入元素,使用带索引的add
    返回数组列表中包含的实际元素数目
    1
    staff.size();
    一旦确定数组大小不在发生变化,可以调用trimToSize方法,将存储区域的大小调整为当前元素数量所需要的存储空间数目 - 访问数组元素 使用get和set方法实现访问或改变数据元素的操作 staff.set(i, harry); 等价于 a[i] = harry 只有i小于或等于数据列表的大小时,才能够调用list.set(i, x); set只能替换数组中已经存在的元素内容 Employee e = staff.get(i) 等价于 Employee e = a[i]; staff.remove(n) 从数组列表中删除一个元素
  • 对象包装器与自动装箱

    有时候需要将int这样的基本类型转换为对象。所有的基本类型都有一个与之对应的类
    定义一个整型数组列表,而尖括号中的类型参数不予许是基本类型,ArrayList<int>, 用Integer对象包装器
    ArrayList<Integer> list = new Arraylist<>();
    有一个很有用的特性,从而更加便于添加int类型的元素到ArrayList<Integer>中
    调用 list.add(3);  将自动地变成  list.add(Integer.valueOf(3)); 这种变换被称为自动装箱
    
  • 参数数量可变的方法

    public PrintStream printf(String fmt, Object... args)
    省略号...是java代码的一部分,表明这个 方法可以接受任意数量的对象(除fmt参数之外)
    
  • 枚举类

    查看定义枚举类型
    public enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE }
    比较枚举类型值, 不需要调用equals 而直接使用 ==
    toString 返回枚举常量名   toString的逆方法是静态方法valueOf
    每个枚举类型都有一个静态的values方法,它将返回一个包含全部枚举值得数组
    Size[] values = Size.values();
    valueOf 返回指定名字,给定类的枚举常量
    ordinal() 返回枚举常量在enum声明中的位置,位置从0开始
    compareTo() 枚举常量在other之前,返回负值,this===other 返回0,否则返回正值
    
  • 继承的设计技巧

    将公共操作和域放在超类
    不要使用受保护的域(同一个包中所有类都可以访问 protected,子类集合无限制,都能派生子类,直接访问protected的实例域)
    使用继承实现is-a关系
    除非所有继承的方法都有意义,否则不要使用继承
    在覆盖方法时,不要改变预期的行为
    使用多态,而非类型信息(if else 定义方法,放置在两个类的超类和接口中,以便使用多态性提供的动态分派机制执行相应操作)
    不要过多地使用反射
    
  • 接口

    - 接口不是类,是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义
    - 接口中所有方法自动地属于public,因此接口声明方法时,不必提供关键字Public
    - 接口中可以定义常量,接口绝不能含有实例域,提供实例域和方法实现的任务应该由实现接口的那个类完成。因此可以将接口看成是没有实例域的抽象类
    - 让类实现一个接口,通常需要两个步骤
            1.将类声明为实现给定的接口
            2.对接口中的所有方法进行定义
            要声明为实现某个接口,需要使用关键字implements
            class Employee implements Comparable
            public int compareTo(Employee other) 实现接口时,必须把方法声明为public
    
    - 接口的特性
            接口不是类,不能使用new运算符实例化一个接口
            不能够构造接口对象,却能声明接口变量  Comparable x
            接口变量必须引用实现了接口的类的对象
            使用instanceof检查一个对象是否属于某个特定的类,也可以检查是否实现了某个特定接口
            尽管每个类智能有一个超类,但却可以实现多个接口,比如拥有克隆和比较的能力
    
    - 接口与抽象类
            使用抽象类表示通用属性存在一个问题,每个类只能扩展一个类,但可以实现多个接口
    
    - 接口与回调
            回调是一种常见的程序设计模式,可以指出某个特定事件发生时应该采取的动作
    
    - lambda表达式
            lambda表达式是一个可传递的代码块,可以在以后执行一次或多次
            lambda表达式形式: 参数,剪头()-> 表达式
            无需指定lambda表达式的返回值,返回类型总是会由上下文推导得出
            lambda表达式只在某些分支返回一个值,而在另外一些分支不返回值是不合法的
    
    - 函数式接口
            对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式,这种接口方式称为函数式接口
    
    - 方法引用
            有时,可能现有的方法可以完成你想要传递到其他代码的某个动作。
            要用::操作符分隔方法名与对象或类名
            object :: instanceMethod
            Class : : static Method
            Class :: instanceMethod
            前两种情况Math::pow等价于(x,y) -> Math.pow(x, y)
            第三种情况String : : compareToIgnoreCase 等同于 ( x , y ) - > x . compareToIgnoreCase ( y )
    
    - 构造器引用
            构造器引用与方法引用类似,只不过方法名为new 例如 Person::new
    
    - 处理lambda表达式
            使用lambda表达式的重点是延迟执行。立即执行代码,直接执行,无需包装在一个lambda表达式中
    
  • 内部类

    内部类是定义在另一个类中的类。使用内部类原因有三点
    1.内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据
    2.内部类可以对同一个包中的其他类隐藏起来
    3.当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷
    
  • 内部类的特殊语法规则

    使用外围类引用的语法,表达式 OuterClass.this  TalkingClock.this.keep
    编写内部对象的构造器 outerObject.new InnerClass  ActionListener listener = this.new TimePrinter();
    在外围类作用域之外,引用内部类 OuterClass.InnerClass
    TalkingClock jabberer = new TalkingClock(1000, true);
    TalkingClock.TimePrinter listener = jabberer.new TimePrinter();
    
  • 局部内部类

    局部类不能用public或者private访问说明符进行声明,它的作用域被限定在声明这个局部类的块中
    局部类有一个优势,即对外部世界可以完全地隐藏起来,即是外围类中的其他代码也不能访问,除了当前方法外,没有任何方法 知道局部类的存在
    
  • 匿名内部类?

  • 局部内部类?
  • 代理?

  • 处理错误

    错误类型
    1.用户输入错误
    2.设备错误
    3.物理限制
    4.代码错误
    
  • 异常分类

    异常对象都派生于Throwable类的一个实例,Throwable分解为Error和Exception
    Exception分解为RuntimeException和IOException(其他异常)
    RuntimeException:  错误的类型转换
                                    数组访问越界
                                    访问null指针
    其他异常: 试图在文件尾部后面读取数据
                    试图打开一个不存在的文件
                    试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在
    
    应该通过检测数组下标是否越界来避免 ArraylndexOutOfBoundsException 异常
    应该通过在使用变量之前检测是否为 null 来杜绝 NullPointerException 异常的发生
    
  • 抛出异常

    1.找到一个合适的异常类
    2.创建这个类的一个对象
    3.将对象抛出
        throw new EOFException
    
  • 创建异常类

    定义一个派生于Exception的类或者子类,习惯上,定义的类包含两个构造器,一个默认构造器,一个带有详细信息的构造器(超类的toString方法将会打印出这些详细信息)
    class FileFormatException extends IOException {
        public FileFormatException() { }
        public FileFormatException(String gripe) {
            super(gripe);
        }
    } 
    
  • 捕获异常

    要想捕获一个异常,必须设置try/catch语句块
    try {
        code
    }
    catch ( ExceptionType e )
    {
        handler for this type
    }
    finally
    {}
    如果在 try 语句块中的任何代码抛出了一个在 catch 子句中说明的异常类 , 那么程序将跳过 try 语句块的其余代码,程序将执行 catch 子句中的处理器代码
    
  • finally子句

    不管是否有异常被捕获,finally子句中的代码都被执行
    
  • 分析堆栈轨迹元素

    堆栈轨迹是一个方法调用过程的列表,他包含了程序执行过程中方法调用的特定位置
    Throwable t = new ThrowableO ;
    StackTraceElement[] frames = t . getStackTrace () ;
    for ( StackTraceElement frame : frames )
        analyze frame
    
  • 基本日志

    生成简单的日志记录,可以使用全局日志记录器
    Logger.getGlobal().info("...");
    
  • 高级日志

    不要将所有的日志都记录到一个全局日志记录器中,而是可以自定义日志记录器
    private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp")
    日志记录级别 SEVERE WARNING INFO CONFIG FINE FINER FINEST。在默认情况下只记录前3个级别
    设置日志级别  logger.setLevel( Level.FINE )
    使用log方法指定级别 logger.log(level.FINE, message);
    记录日志常见用途是记录那些不可预料的异常,可以使用下面两个方法
    IOException exception = new IOException ( " . . . " ) ;
    logger.throwing("com.mycompany.mylib.Reader " , "read", exception);
    throw exception
    或者
    catch(IOException e) {
        Logger.getLogger("com.mycompany.myapp").log(level.WARNING, "Reading image", e);
    }
    
  • 修改日志管理器配置

    可以通过编辑配置文件来修改日志系统的各种属性,默认情况,配置文件存在于: jre/lib/logging.properties。要想使用另一个配置文件,就要将java.util.logging.config.file特性设置为配置文件的存储位置,并用命令启动: java -Djava.tuil.logging.config.file=configFile MainClass
    
  • 日志消息本地化?

  • 日志处理器?

  • 日志过滤器?

  • 日志格式化器?

  • 调试技巧

    一个不太为人所知却很有效的技巧是在每一个类中放置一个单独的main方法。这样就可以对每一个类进行单元测试
    
  • 泛型程序设计

    泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用
    
  • 定义简单的泛型类

    一个泛型类就是具有一个或多个类型变量的类  public class Pair<T, U>
    用具体的类型替换类型变量就可以实例化泛型类型如  Pair<String>
    
  • 泛型方法

    public static <T> T getMiddle(T...a)
    
  • 泛型约束及局限性

    1.不能用基本类型实例化类型参数
    2.运行时类型查询只适用于原始类型(虚拟机中的对象总有一个特定的非泛型类型,因此所有查询只产生原始类型) if(a instanceof Pair<String>)//Error
    3.不能创建参数化类型的数组  pair<String>[] table = new Pair<String>[10]  // Error
        类型擦除后,table的类型是Pair[]可以把它转换为Object[] obj[0] = "hello"  // Error
    4.Varargs警告,java不支持泛型类型的数组
    5.不能实例化类型变量   new T(..)
    6.不能构造泛型数组。 数组本身也有类型,监控存储在虚拟机中的数组。这个类型会被擦除
    7.泛型类的静态上下文中类型变量无效  public static T singleInstance // Error
    8.不能抛出和捕获泛型类对象 catch(T e)  T extends Exception //  Error
    9.可以消除对受查异常的检查
    10.注意擦除后的冲突,当泛型被擦除时,无法创建引发冲突的条件
    
  • 泛型类型的继承规则

    Pair<Manager>和Pair<Employee> 不是子类
    
  • 通配符类型

    固定的泛型类型系统使用起来并不愉快,java发明了巧妙的解决方案
    通配符类型中,允许类型参数变化  Pair<? extends Employee>
    表示任何泛型Pair类型, 它的类型参数是Employee的子类
    
  • 通配符的超类型限定

    通配符限定与类型变量限定十分 类似,还有一个附加的能力,即可以指定一个超类型限定 
    ? super Manager
    这个通配符限制为Manager的所有超类型
    
  • 集合

    队列  先进先出
    队列通常有两种实现方式  1.使用循环数组  2.使用链表
    循环数组是一个有界集合,即容量有限。如果程序要收集的对象数量没有上限,就最好用链表实现
    
    collection接口
            集合类的基本接口是Collection接口
            add方法用于向集合添加元素 如果添加元素确实改变了集合就返回true,没有就返回false,集合中不允许有重复对象
            iterator方法用于返回一个实现了Iterator接口的对象,可以使用这个迭代器对象依次访问集合中的元素
    
    迭代器
            Iterator接口包含四个方法
            通过调用next方法,逐个访问集合中的子元素,到达末尾,next方法抛出一个NoSuchElementException,因此 需要在调用next之前调用hasNext方法
            用for each循环可以更加简单的调试循环操作,编译器简单地将for each循环翻译为带有迭代器的循环
            for (String element : c)
            对ArrayList进行迭代,迭代器从索引0开始,访问HashSet中元素,每个元素将会按照某种随机次序出现
            java迭代器认为位于两个元素之间,当调用next,迭代器就越过下一个元素,并返回越过的元素的引用
            Iterator接口的remove方法将会删除上次调用next方法时返回的元素,
    
    泛型使用方法
            由于Collection和Iterator都是泛型接口,可以编写操作任何集合类型的使用方法
            int Size()
            boolean isEmpty()
            boolean contains(Object obj)
            boolean equals(Object other)
            boolean addAll(Collection<?> c)
            boolean remove(Object obj)
            void clean()   ...
            实现Collection接口的每一个类都要提供如此多的方法很烦,为了容易使用,提供了一个类AbstractCollect
    
    集合框架中的接口
            java集合框架为不同类型的集合定义了大量接口
            插入元素 boolean add(E element) 不过,由于键/值对,所以要用put方法来插入:
            V put(K key, V value)要从集合中读取元素,可以用迭代器访问元素,从映射中读取用get方法
            V get(K key)
            List是一个有序集合,元素会增加到容器中的特定位置,使用迭代器访问,或者一个整数的索引访问
            ListIterator接口时Iterator的一个子接口,add方法用于在迭代器位置前面增加一个元素
            Set接口等同于Collection接口,集(set)的add方法不允许增加重复的元素,要适当的定义equals方法,只要包含同样的元素就认为是相等的,而不要求有同样的顺序。
            概念上讲,并不是所有集合都是集
    

    具体的集合

  • 链表
    数组和数组列表有一个重大缺陷,就是从数组的中间位置删除一个元素付出很大的代价,删除/添加元素后所有之后的所有元素都要前/后移动。链表解决了这种问题
    链表都是双向链接的
    
链表是一个有序集合,LinkedList.add方法将对象添加到链表尾部,常常需要将元素添加到中间,所以这种依赖于位置的add方法将由迭代器负责

数组列表
        List接口用于描述一个有序集合,有两种访问协议,一种迭代器,一种能够get set随机访问,集合类库提供了ArrayList类,这个类也实现了List接口

散列集
        可以快速地查找所需要的对象,这就是散列集。散列集为每个对象计算一个整数,称为散列码,散列码由对象的实例域产生的一个整数,具有不同数据域的对象将产生不同的散列码
        java提供了一个HashSet类,它实现了基于散列表的集

树集
        是一个有序集合,可以任意顺序将元素插入集合中,对元素遍历时,每个值将自动地按照排序后的顺序呈现。(元素自动排序)

双端队列 (Deque接口),可以让人有效地在头部和尾部同时添加或删除元素,不支持在中间添加元素

优先级队列
        优先级队列中的元素可以按照任意的顺序插入,却总按照排序的顺序进行检索,无论何时调用remove方法,总会获得当前优先级队列中最小的元素,优先级队列使用了一个 优雅且高效的数据结构,称为堆。
        堆是一个可以自我调整的二叉树,对树执行添加和删除操作,可以让 最小的元素移动到根
  • 映射

    通常我们知道某些健的信息,并想要查找与之对应的元素。映射数据结构就是为此设计,映射用来存放键/值对,如果提供了键就能查找到值
    java类库为映射提供了两个 通用实现,HashMap和TreeMap,这两个类都实现了Map接口
    与集一样,如果不需要排列顺序访问键,就最好选择散列
    Map<String , Employee> staff = new HashMap <> ( )
    get  put  remove 
    要迭代处理映射的键和值,最容易的方法是使用forEach方法,可以提供一个接受键值得lambda表达式
    scores.forEach ( ( k , v ) - >
        System . out . println ( " key = " + k + " , value : " + v ))
    
  • 视图与包装器

  • 轻量级集合包装器

    Arrays类的静态方法asList将返回一个包装了普通java数组的List包装器,这个 方法可以将数组传递给一个 期望得到列表或集合参数的方法
    

    Card[ ] cardOeck = new Card[52]
    List cardList = Arrays.asList (cardDeck) :

  • 子范围

    可以为很多集合建立子范围,假设有个列表staff,想从中取出第10-19个元素
    List group = staff.subList (10 , 20)  第一个索引包含在内,第二个索引不包含在内
    
  • 算法

    排序与混排
            Collection类中的sort方法可以实现了List接口的集合进行排序,降序排序Collectitons.reverseOrder
            Collection类中的shuffle,功能与排序相反,即随机地混排列表中元素的顺序
    
    二分查找
            Collection类的binarySearch方法。集合必须是拍好序的。返回负值,则没有匹配的元素,大于等于0,则表示匹配对象的索引。如果集合没有采用Comparable接口的compareTo方法进行排序,就还要提供一个比较器对象。
    
    简单算法
            min最小值
            max最大值
            copy负值所有元素到目标
            fill将列表中所有位置设置为相同的值
            addAll 将所有值添加到集合中,如果集合改变 返回true
            replaceAll用newValue取代所有值为oldValue的元素
            swap交换给定偏移量的两个元素
            reverse逆置列表中元素的顺序
            rotate旋转列表中的元素,将索引i的条目移动到位置(i+d)%I.size
            disjoint 如果两个集合没有共同的元素,则返回true
            removeIf 删除所有匹配的元素
    
    批操作
            coll1.removeAll(coll2) 将从coll1中删除coll2中出现的所有元素
            coll1.retainAll(coll2)将从coll1中删除所有未在coll2中出现的元素
    
    集合和数组的转换
            数组转换为集合 Arrays.asList包装器
            HashSet<String> staff = new HashSet<>(Arrays.asList(values))
            集合转换为数组 会更困难一些,可以是用toArray
            Object[] values = staff.toArray();不过这样做的结果是一个对象数组,尽管你知道集合中包含一个特定类型的对象,但不能使用强制类型转换。toArray返回的数组是一个Object[]数组,不能改变类型
            String[] values = staff.toArray(new String[0])
            String[] values = staff.toArray(new String[staff.size()]) 指定大小数组
    
CATALOG
  1. 1. java核心技术卷-基础知识 | 8.0