详尽笔记,用于复习和速查
  将包含基础、进阶、高阶
  目前更新到数据结构-JCF-Set


概述

  • Java SE(Standard Edition) 面向PC级应用开发
  • Java EE(Enterprise Edition) 面向企业级应用开发
  • Java ME(Micro Edition) 面向嵌入式应用开发

环境搭建

JDK下载

搜索java archive,选择版本下载。(本文选择JDK1.8)
安装JDK时,会同时安装JRE。

配置环境变量

新增
JAVA_HOME
C:\Java\jdk1.8.0_73(jdk地址)
修改Path
新增%JAVA_HOME%\bin 上移到最高

打开cmd
java -version
有版本号输出
javac
有用法输出
即可。

配置Eclipse

修改字号
Window-Preferences-General-Appaerance-Colors and Fonts-右侧Java-Java Editor Text Font

显示行号
General-Editors-Text Editors-右侧show line numbers勾上

自动保存时间间隔
General-Workspace-右侧Workspace save interval(in minutes)(默认5分钟)
下方可以修改文本编码格式

复原视窗
Window-Perspective-Reset Perspective…

Eclipse使用教程

Java从编写到运行

编写:利用记事本/vi/IDE等完成代码文件(.java)编写
编译:利用JDK中javac.exe将代码(.java)编译成字节码文件(.class)
运行:java.exe读入并解释字节码文件(.class),最终在JVM上运行

编程标准

  • 类名首字母大写,后面单词驼峰命名
  • 方法名和变量名首字母小写,后面单词驼峰命名
  • 阿里巴巴编程标准

基础知识

class类

  • java文件必须以.java作为扩展名
  • 一个java文件只能有一个public class
  • public class的名字必须和文件名字一样,大小写都要完全一致
  • 一个文件可有多个class,但是只能有一个是public。不提倡一个文件里面放多个类(内部类除外)
  • System.out.print 输出
  • System.out.println 换行输出

  • 类是Java中最基础逻辑单位
    Java所有内容都需放在类的范围中

  • 类的构成
    成员变量/属性
    成员方法/函数

main函数

  • 一个class最多只能有一个main函数(经指正,一个类可以有多个main函数-通过重载,但只能有一个入口函数)
    类可以没有main函数,没有main函数的类不能主动执行,但可以被动执行(被调用执行)
  • 程序入口都是main函数
  • main函数的形参和前缀修饰符public、static、void等都不能省略,是固定写法
    注意区别

    1
    2
    3
    4
    5
    6
    7
    8
    public static void main(String[] args)
    {
    //PSVM,main函数
    }
    public void main()
    {
    //普通main函数,不是入口函数
    }
  • 严格意义上说,main函数不属于这个类的所拥有函数。
    只是因为Java要求所有内容都必须放置在class范围内,所有main函数“寄居”在某一个class中。

  • main函数无法被其它方法/类所调用。
  • 一个Java程序可调用多个其它Java class。
  • String[] args是main函数的形参,即在main函数中可使用args的值(这些值在main函数启动时输入)。

右键test.java,Run As->Run Configuration-右侧Arguments-输入Program arguments程序参数-Run
多个变量需用空格隔开

八种基本类型

  • boolean 布尔
    true false(默认)
  • byte 字节
    1 byte = 8 bits
    存储有符号的,以二进制补码表示的整数
    最小值-128,最大值127,默认值0
    该类型用在大型数组中可显著节约空间(占用空间只用int类型的四分之一),主要代替小整数
    在二进制文件读写中使用较多
    1
    2
    3
    4
    byte a = (byte) -129;
    System.out.println(a);//127
    byte b = (byte) 128;
    System.out.println(b);//-128

怎么理解:
标准值:-128~127
两边可以延伸
左延伸-384(-128)~-129(127)
右延伸128(-128)~383(127)

  • short/int/long 短整数/整数/长整数
    short 16位 2个字节 -32768~32767,-2^15~2^15-1,默认值0
    int 32位,4个字节 -2147483648~2147483647,-2^31~2^31-1,默认值0
    long 64位 8个字节 -9223372036854775808~9223372036854775807,-2^63~2^63-1,默认值0L
  • float/double 浮点数
    float 单精度 32位 4个字节 1.410^-45~3.410^38 默认值0.0f
    double 双精度 64位 8个字节 4.910^-324~1.710^308 默认值0.0d
    float和double都不能用来表示很精确的数字

  • char 字符
    16位 Unicode字符
    最小值是 \u0000(即为0)
    最大值是\uffff(即为65535)
    \u4e00~\u9fa5两万多汉字)

运算符

>> 右移 除以2
<< 左移 乘以2

自定义函数

重载(overload):同一个类中,函数名称可以相同,但函数参数的个数或者类型必须有所不同
不能以返回值来区分同名的函数

面向对象

对象=属性+方法
对象的规范=属性定义+方法定义

1
A obj = new A();

  • obj可看作是内存中一个对象(包含若干个数据)的句柄
  • 在C/C++中,obj称为指针,在Java中称为Reference
  • 对象赋值是Reference赋值,基本类型是直接值拷贝
    理解:基本类型的变量值小,可直接拷贝;对象包含多个值,不容易复制,赋值采用共享同一块内存区域。

函数内的局部变量,编译器不会给默认值,需初始化;
类的成员变量,编译器会给默认值,可直接使用。

构造函数

  • 构造函数名称必须和类名一样,且没有返回值
    public void A(int x)不是构造函数,而是普通函数。
  • Java有构造函数,但没有析构函数
  • 每个变量都有生命周期,它只能存储在离它最近的一对{}中(经指正,此条不够严谨)
  • Java具有内存自动回收机制,当变量退出其生命周期后,JVM会自动回收所分配的对象的内存。
  • 对象回收效率依赖于垃圾回收器GC(Garbage Collector),其回收算法关系到性能好坏,是JVM研究热点。
  • 每个Java类都必须有构造函数
  • 若没有显式定义构造函数,Java编译器自动为该类产生一个空的无形参构造函数。若有显式构造函数,编译器就不会再产生构造函数
  • 每个子类的构造函数的第一句话,都默认调用父类的无参数构造函数super(),除非子类的构造函数第一句话是super,而且super语句必须放在第一条
  • 一个类可有多个构造函数,只要形参列表不同

多个class可写在一个.java文件中,但最多只有一个类是public class,并且public class类的名字必须和.java文件名相同。

信息隐藏原则

  • 类的成员属性,是私有的private
  • 类的方法,是共有的public,通过方法修改成员属性的值
  • get和set方法是公有public的,统称为getter和setter
  • 外界对类成员的操作只能通过get和set方法
  • 可用Java IDE快速生成
    Source-Generate Getters and Setters…

this

  • this负责指向本类中的成员变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Test{
    private int id;

    public Test(int id){
    //在这个构造函数里,形参优先级更高
    this.id = id;
    }

    public int getId(){
    return id;
    }
    }
  • this负责指向本类中的成员方法

    1
    this.add(1,2);//调用本类的add方法,this可忽略
  • this可代替本类的构造函数

    1
    this(5);//调用本类的一个形参的构造函数

继承、接口和抽象类

继承

父类/基类/超类
Parent class/Base class/Super class
子类/派生类
Child class/Derived class

  • 子类继承父类所有的属性和方法(但不能直接访问private成员)
  • 根据信息隐藏原则:子类会继承父类所有的方法,可直接使用
  • 子类也会继承父类的父类所有的属性和方法(但不能直接访问private成员)
    子类可以通过调用父类的方法来访问父类的私有的成员属性
    在同样方法名和参数情况下,本类的方法会比父类的方法优先级高

区别于C++,单根继承

  • 单根继承原则:每个类都只能继承一个类
  • 若不写extends,Java类默认继承java.lang.Object类
  • Java所有类从java.lang.Object开始,构造出一个类型继承树
  • Object类里面默认就有clone, equals, finalize, getClass, hashCode, toString等方法

  • 每个子类的构造函数的第一句话,都默认调用父类的无参数构造函数super(),除非子类的构造函数第一句话是super,而且super语句必须放在第一条
    如果构造函数的第一句话不是super,编译器会自动增加一句super();。如果构造函数第一句是自己写的super语句,编译器就不会自动添加。

抽象类

若方法只有方法名字,形参列表,没有方法体,那所在的类就被定义为抽象类。
若一个类暂时有方法未实现,需被定义为抽象类。

  • 一个类继承于抽象类,就不能继承于其他的(抽象)类
  • 子类可继承于抽象类,但一定要实现父类们所有abstract的方法,若不能完全实现,子类也必须被定义为抽象类

接口

  • 如果类的所有方法都没有实现,这个类就算是接口interface

    1
    2
    3
    4
    public interface Animal{
    public void eat();
    public void move();
    }
  • 类只可以继承(extends)一个类,但可实现(implements)多个接口,继承和实现可以同时

  • 接口不算类,或者说是特殊的类
  • 接口可以继承(多个)接口,没有实现的方法将会叠加
  • 类实现接口,就必须实现所有未实现的方法。若没有全部实现,只能成为一个抽象类
  • 接口里可定义变量,但一般是常量 (经指正,成员变量只能是常量)

总结

抽象类和接口相同点:两者都不能被实例化,不能new操作
不同点:

  • 抽象类abstract,接口interface
  • 抽象类可有部分方法实现,接口所有方法不能有实现 (经指正,Java8后,接口可有默认实现)
  • 一个类只能继承(extends)一个(抽象)类,实现(implenments)多个接口
  • 接口可继承(extends)多个接口
  • 抽象类有构造函数,接口没有构造函数
  • 抽象类可有main,也能运行,接口没有main函数
  • 抽象类方法可有private/protected,接口方法都是public

类转型

类型可相互转型,但只限制于有继承关系的类

  • 子类可转换成父类(从大到小,向上转型),而父类不可转为子类(从小变大,向下转型)
  • 父类转为子类有一种情况例外:这个父类本身就是从子类转化而来
    1
    2
    Human obj1 = new Man();//OK, Man extends Human
    Man obj2 = (Man) obj1;//OK, because obj1 is born from Man class

多态

  • 类型转换带来的作用就是多态
  • 子类继承父类的所有方法,但子类可重新定义一个名字,参数和父类样的方法,这种行为是重写(覆写,覆盖,overwrite/override,not overload(重载))
  • 子类方法优先级高于父类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Man extends Human{
    public void eat(){
    System.out.println("I can eat more");
    }
    public void plough() { }

    public static void main(String[] a){
    Man obj1 = new Man();
    obj1.eat();//call Man.eat()
    Human obj2 = (Human) obj1;
    obj2.eat();//call Man.eat()
    Man obj3 = (Man) obj2;
    obj3.eat();//call Man.eat()
    }
    }

契约设计:类不会直接使用另外一个类,而是采用接口的形式,外部可以“空投”这个接口下的任意子类对象

static、final和常量设计

static

static关键字可作用在:

  • 变量
    static变量只依赖于类存在(通过类即可访问),不依赖于对象实例存在。
    所有对象实例,关于同一个变量的值都共享存储在一个共同的空间(栈)。

  • 方法
    静态方法无需通过对象来引用,而通过类名可直接引用。
    静态方法中只能使用静态变量,不能使用非静态变量。
    静态方法禁止引用非静态方法。

  • 匿名方法块
    static块只在类第一次被加载时调用,在程序运行期间,代码只运行一次
    执行顺序:static块 > 匿名块 > 构造函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class StaticBlock
    {
    static
    {
    System.out.println("static 块");
    }
    {
    System.out.println("匿名块");
    }
    public StaticBlock()
    {
    System.out.println("构造函数");
    }
    {
    System.out.println("匿名块");
    }
    }

单例模式

  • 又名单态模式,Singleton
  • 限定某一个类在整个程序运行过程中,只能保留一个实例对象在内存空间
  • GoF的23中设计模式(Design Pattern)(创建型、结构型、行为型)中经典的一种,属于创建型模式类型
  • 采用static来共享对象实例
  • 采用private构建函数,防止外界new操作

final

final关键字用来修饰

  • 方法
  • 字段

  • final的类,不能被继承(不能有子类)

  • 父类中若有final方法,子类不能改写此方法
    子类方法和父类方法在方法名和参数列表一样,就属于方法改写/重写/覆写,若参数列表不一样,就属于方法重载
  • final的变量不能再次赋值
    若是基本类型的变量,不能修改其值
    若是对象实例,不能修改其指针(但可修改对象内部的值)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class FinalObject
    {
    int a = 10;
    }
    public class FinalObjectTest{
    public static void main(String[] args){
    final FinalObject obj1 = new FinalObject();
    System.out.println(obj1.a);
    obj1.a = 20;
    System.out.println(obj1.a);//正确,可以修改内部值

    obj1 = new FinalObject();//报错,不能修改指针
    }
    }

常量设计

  • Java没有constant关键字
  • 不能修改, final
  • 不会修改/只读/只要一份, static
  • 方便访问public

Java中的常量

  • public static final
  • 建议变量名字全大写,以连字符相连,如UPPER_BOUND
  • 一种特殊的常量:接口内定义的变量默认是常量

常量池

  • Java为很多基本类型的包装类/字符串都建立常量池
  • 常量池:相同的值只存储一份,节省内存,共享访问
  • 基本类型的包装类
    Boolean: true, false
    Byte:-128~127
    Short, Integer, Long:-128~127
    Character:0~127
    不包括Float, Double:没有缓存(常量池)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Long l1 = -128L;
    Long l2 = -128L;
    System.out.println(String.valueOf(l1==l2));//true

    Float f1 = 0.5f;
    Float f2 = 0.5f;
    System.out.println(String.valueOf(f1 == f2));//false

    Double d1 = 0.5;
    Double d2 = 0.5;
    System.out.println(String.valueOf(d1 == d2));//false
  • Java为常量字符串都建立常量池缓存机制

  • 基本类型的包装类和字符串有两种创建方式

  1. 常量式(字面量)复制创建,放在栈内存(将被常量化)

    1
    2
    Integer a = 10;
    String b = "abc";
  2. new对象进行创建,放在堆内存(不会常量化)

    1
    2
    Integer c = new Integer(10);
    String d = new String("abc");

这两种创建方式导致创建的对象存放的位置不同
栈内存读取速度快但容量小
堆内存读取速度慢但容量大

  • 基本类型和包装类比较,将对包装类自动拆箱
  • 对象比较,比较地址
  • 加法+会自动拆箱

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    int i1 = 10;
    Integer i2 = 10;//自动装箱
    System.out.println(i1 == i2);//true
    //自动拆箱 基本类型和包装类进行比较,包装类自动拆箱

    Integer i3 = new Integer(10);
    System.out.println(i1 == i3);//true
    //自动拆箱 基本类型和包装类进行比较,包装类自动拆箱

    System.out.println(i2 == i3);//false
    //两个对象比较,比较其地址
    //i2是常量,放在栈内存常量池中,i3是new出对象,放在堆内存中

    Integer i4 = new Integer(5);
    Integer i5 = new Integer(5);
    System.out.println(i1 == (i4+i5));//true
    System.out.println(i2 == (i4+i5));//true
    System.out.println(i3 == (i4+i5));//true
    //i4+i5操作将会使得i4,i5自动拆箱为基本类型并运算得到10
    //基础类型10和对象相比,将会使对象自动拆箱,做基本类型比较

    Integer i6 = i4 + i5;//+操作使得i4,i5自动拆箱,得到10,因此i6 == i2
    System.out.println(i1 == i6);//true
    System.out.println(i2 == i6);//true
    System.out.println(i3 == i6);//false
  • 常量赋值(堆内存)和new创建(栈内存)不是同一个对象

  • 编译器只会优化确定的字符串,并缓存
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    String s0 = "abcdef";
    String s1 = "abc";
    String s2 = "abc";
    String s3 = new String("abc");
    String s4 = new String("abc");
    System.out.println(s1 == s2);//true常量池
    System.out.println(s1 == s3);//false一个栈内存,一个堆内存
    System.out.println(s3 == s4);//false两个都是堆内存

    String s5 = s1 + "def";//设计到变量,编译器不优化
    String s6 = "abc" + "def";//都是常量,编译器会自动优化成abcdef
    String s7 = "abc" + new String("def");//设计到new对象,编译器不优化
    System.out.println(s5 == s6);//false
    System.out.println(s6 == s7);//false
    System.out.println(s0 == s6);//true

    String s8 = s3 + "def";//涉及到new对象,编译器不优化
    String s9 = s4 + "def";//设计到new对象,编译器不优化
    String s10 = s3 + new String("def");//涉及到new对象,编译器不优化
    System.out.println(s8 == s9);//false
    System.out.println(s8 == s10);//false
    System.out.println(s9 == s10);//false

不可变对象和字符串

不可变对象

  • 不可变对象(Immutable Object)
    一旦创建,该对象(状态/值)不能被更改
    其内在的成员变量的值不能修改
    如八个基本型别的包装类
    String, BigInteger和BigDecimal等
  • 可变对象(Mutable Object)
    普通对象
    1
    2
    3
    4
    5
    String a = new String("abc");
    String b = a;
    System.out.println(b);//abc
    a = "def";
    System.out.println(b);//abc

不可变对象是指值对象不再修改,即abc不会被修改,而指针(句柄/变量名)a的指向可以修改。
不可变对象,也是传指针(引用)
由于不可变,临时变量指向新内存,外部实参的指针不改动

1
2
3
4
5
6
7
public static void change(String b)
{
b = "def";
}
a = new String("abc");
change(a);
System.out.println(a);//abc

a->abc
b–^
change(a)后
a->abc
b->def

  • 如何创建不可变对象
    immutable对象是不可改变,改变要clone/new一个对象进行修改
    所有属性是final和private
    不提供setter方法
    类是final的,或所有方法都是final
    勒种包含mutable对象,那么返回拷贝需要深度clone

  • 不可变对象(Immutable Object)优点
    只读,线程安全
    并发读,提高性能
    可重复使用

  • 缺点
    制造垃圾,浪费空间

字符串

不可变对象
字符串内容比较:equals方法
是否指向同一个对象:指针比较==

  • 字符串加法
    1
    2
    String a = "abc";
    a = a + "def";//String不可修改,需新申请空间,效率差

使用SrringBuffer/StringBuilder类的append方法进行修改
StringBuffer/StringBuilder的对象都是可变对象
StringBuffer(同步,线程安全,修改快速),StringBuilder(不同步,线程不安全,修改更快)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void changeValue(int a)
{
a = 10;
}
public static void changeValue(String s1)
{
s1 = "def";
}
public static void changeValue(StringBuffer s1)
{
s1.append("def");
}

public static void main(String[] args)
{
int a = 5;
String b = "abc";
StringBuffer c = new StringBuffer("abc");
changeValue(a);
changeValue(b);
changeValue(c);
System.out.println(a);//5
System.out.println(b);//abc
System.out.println(c);//abcdef
}

package, import和classpath

package和import

  • 若所有Java类都是放置在同一个目录下的,类之间相互调用无需显式声明调用
    同一个目录下,两个类的名字不能相同
    文件过多不方便查找
  • Java支持多个目录放置Java,并通过package/import/classpath/jar等机制配合使用,可支持跨目录放置和调用Java类
    1
    2
    3
    4
    package cn.edu.pku;
    public class PackageExample
    {
    }

类全称cn.edu.pku.PackageExample,短名称PackageExample
引用类时,必须采用全称引用;程序正文可用短名称
PackageExample.java必须严格放置在cn/edu/pku目录下

  • 包名尽量唯一
  • 域名是唯一的,因此常用域名做包名
  • 域名逆序:cn.edu.pku,范围通常从大到小
    类的完整名字:包名+类名,cn.edu.pku.PackageExample
    包名:和目录层次一样,cn\edu\pku\PackageExample.java
    包具体放在上面位置不重要,编译和运行时再制定

举个栗子

1
2
3
4
5
6
7
8
9
10
11
12
package cn.edu.pku;
import cn.edu.pku.PackageExample;
//或import cn.edu.pku.*
//但不能是import cn.*
//若PackageExample和当前类在同一个目录,可省略import

public class PackageExampleTest{
public static void main(String[] args){
PackageExample obj = new PackageExample();
//此处可用类的短名称来引用
}
}

“*”代表这个目录下所有文件,但不包括子文件夹和子文件夹内的文件。

import规则

  • import必须全部放在package之后,类定义之前
    package必须是第一句话
  • 多个import的顺序无关
  • 可用来引入一个目录下的所有类,比如import java.lang.;
    注意:不能递归包含其下各个目录下的文件
  • import尽量精确,不推荐*,以免新增同名程序会使老程序报错

jar

用于可执行程序文件的传播,实际上是一组class文件的压缩包
项目引入一个jar文件,就可以使用jar文件中所有类(.class文件),无需类的源码(.java文件)。
jar只包含.class文件,没有.java文件,不会泄露源码。不过Java里的一些反编译工具,可从class反编译到Java。

利用Eclipse的Export功能导出jar文件

  • 选中项目,点击顶部菜单File-Export-Java-Jar File

classpath

手动创建c:\temp\cn\com\test\Man.java
编译:

1
javac c:\temp\cn\com\test\Man.java

运行:

1
java -classpath   .;c:\temp   cn.com.test.Man

-classpath固定格式参数,简写为-cp
.;c:\temp
windows是分号,Linux/Mac是冒号
当在某一个子路径找到所需类后,后续子路径不再寻找
在classpath中,子路径排在前面优先级高
当所有子路径都寻找不到所需类,系统会报告NoClassDefFoundException错误

编译和运行规则

  • 编译一个类,需java文件全路径,包括拓展名
  • 运行一个类,需写类名全称(非文件路径),无需写扩展名
  • 编译类时,需给出这个类所依赖的类(包括依赖的类再次依赖的所有其他类)的所在路径
  • 运行类时,需给出这个类,以及被依赖类的路径总和
  • classpath参数也可以包含jar包。若路径内有空格,请将classpath参数整体加双引号。
    1
    java -classpath   ".;c:\test.jar;c:\temp;c:\a bc"   cn.com.test.Man

Java访问权限

  • private:私有,只能本类访问
  • default(通常忽略不写):同一个包内访问
  • protected: 同一个包,子类均可以访问
  • public:公开,所有类都可访问
    四种都可用来修饰成员变量、成员方法、构造函数
    default和public可修饰类
    1
    2
    class A { }//default class
    public class B{ }//public class

权限表
同一个类 同一个包 不同包的子类 不同包的非子类
private √
default √ √
protected √ √ √
public √ √ √ √

Java常用类

Java类库文档
https://docs.oracle.com/en/
https://docs.oracle.com/en/java/javase/13/docs/api/index.html
这些文档原先是程序中的注释。利用JavaDoc技术,将这些注释抽取出来,组织形成的以HTML为表现形式的API文档。

数字类

  • Java数字类
    整数Short, Int, Long
    浮点数Float, Double
    大数类BigInteger(大整数), BigDecimal(大浮点数)
    大数类没有限制,可表示无穷大的数字
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    BigInteger b1 = new BigInteger("123456789");
    BigInteger b2 = new BigInteger("987654321");
    System.out.println(b2.add(b1));//加法操作
    System.out.println(b2.subtract(b1));//减法操作
    System.out.println(b2.multiply(b1));//乘法操作
    System.out.println(b2.divide(b1));//除法操作
    System.out.println(b2.max(b1));//求出最大数
    System.out.println(b2.min(b1));//求出最小数
    BigInteger result[] = b2.divideAndRemainder(b1);//求出余数的除法操作
    System.out.println("商是:" + result[0] + ";余数是:" + result[1]);//求出余数的除法操作
    System.out.println(b1.equals(b2));//是否等价
    int flag = b1.compareTo(b2);
    if(flag == -1)
    System.out.println("比较操作:b1<b2");
    else if(flag == 0)
    System.out.println("比较操作:b1==b2");
    else
    System.out.println("比较操作:b1>b2");

尽量采用字符串对BigDecimal赋值,这样精度更准确。

1
2
3
4
5
6
7
8
System.out.println(new BigDecimal("2.3"));
System.out.println(new BigDecimal(2.3));//2.29999999999999

BigDecimal num1 = new BigDecimal("10");
BigDecimal num2 = new BigDecimal("3");
//需要指定位数,防止无限循环,或者包含在try-catch中
BigDecimal num3 = num1.divide(num2, 3, BigDecimal.ROUND_HALF_UP);
System.out.println(num3);

BigDecimal做除法操作,需要注意截断,防止出现无限循环小数。

随机数类Random
nextInt() 返回一个随机Int
nextInt(int a) 返回一个[0,a)之间的随机int
nextDouble() 返回一个[0.0,1.0]之间double
ints方法批量返回随机数数组
Math.random() 返回一个[0.0,1.0]之间double

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//第一种,采用Random类随机生成
Random rd = new Random();
System.out.println(rd.nextInt());
System.out.println(rd.nextInt(100));//0-100的随机数
System.out.println(rd.nextLong());
System.out.println(rd.nextDouble());

//第二种,生成一个范围内的随机数 例如0-10之间的随机数
System.out.println(Math.round(Math.random()*10));

//JDK 8 新增方法
rd.ints();//返回无限个int类型范围内的数据
int[] arr = rd.ints(10).toArray();//生成10个int范围类的个数
for(int i = 0;i < arr.length; i++){
System.out.println(arr[i]);
}

arr = rd.ints(5, 10, 100).toArray();//5个10-100的随机数
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}

arr = rd.ints(10).limit(5).toArray();
for(int i = 0;i < arr.length; i++){
System.out.println(arr[i]);
}

工具类Math
java.math包
java.lang.Math
绝对值函数abs
对数函数log
比较函数max、min
幂函数pow
四舍五入函数round
向下取整floor
向上取整ceil

字符串相关类

String
Java使用频率最高的类
一个不可变对象,加减操作性能较差
常用:
charAt, concat, contains, endsWith, equals, equalsIgnoreCase, hashCode, indexOf, length, matches, replace, replaceAll, split, startsWith, subString, trim, valueOf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
String a = "123;456,789,123 ";
System.out.println(a.charAt(0));//返回第0个元素
System.out.println(a.indexOf(";"));//返回第一个;的位置
System.out.println(a.concat(";000"));//连接一个新字符串并返回,a不变
System.out.println(a.contains("000"));//判断a是否包含000
System.out.println(a.endWith("000"));//判断a是否以000结尾
System.out.println(a.equals("000"));//判断是否等于000
System.out.println(a.equalsIgnoreCase("000"));//判断在忽略大小写情况下是否等于000
System.out.println(a.length());//返回a长度
System.out.println(a.trim());//返回a去除前后空格后的字符串,a不变
String[] b = a.split(";");//将a字符串按照;分割成数组
for(int i = 0; i < b.length; i++){
System.out.println(b[i]);
}

System.out.println(a.substring(2, 5));//截取a的第2个到第5个字符 a不变
System.out.println(a.replace("1", "a"));
System.out.println(a.replaceAll("1", "a");//replaceAll第一个参数是正则表达式

String s1 = "123456789";
String s2 = s1.replace("?", "a");
String s3 = s1.replaceAll("[?]", "a");
//这里的[?]才表示字符问号,这样才能正常替换。不然在正则中会有特殊的意义就会报异常

trim方法去掉的是字符串的前后空格,无法去除字符串中间的空格。

  • 可变字符串
    StringBuffer(字符串加减,同步,性能好)
    StringBuilder(字符串加减,不同步,性能更好)

  • StringBuffer/StringBuilder:方法一样,区别在同步
    append/insert/delete/replace/substring
    length字符串实际大小,capacity字符串占用空间大小
    trimToSize():去除空隙,将字符串存储压缩到实际大小
    如有大量append,事先预估大小,再调用相应构造函数

时间类

  • java.util.Date(基本废弃,Deprecated)
    getTime(),返回自1970.1.1以来的毫秒数
  • java.spl.Date(和数据库对应的时间类)
  • Calendar是目前程序中最常用的,但是是抽象类
    Calendar gc = Calendar.getInstance();
    Calendar gc = new GregorianCalendar();
    简单工厂模式
    查看CalendarClassTest.java
  • Calendar
    get(Field) 来获取时间中每个属性的值,注意,月份0-11
    getTime() 返回相应的Date对象
    getTimeInMillis() 返回自1970.1.1以来的毫秒数
    set(Field) 设置时间字段
    add(field, amount) 根据指定字段增加/减少时间
    roll(field, amount) 根据指定字段增加/减少时间,但不影响上一级的时间段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    Calendar calendar = Calendar.getInstance();

    public void test1(){
    //获取年
    int year = calendar.get(Calendar.YEAR);
    //获取月
    int month = calendar.get(Calendar.MONTH) + 1;
    //获取日
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    //获取时
    int hour = calendar.get(Calendar.HOUR);
    //获取分
    int minute = calendar.get(Calendar.MINUTE);
    //获取秒
    int second = calendar.get(Calendar.SECOND);
    //星期,从星期日算起
    int weekday = calendar.get(Calendar.DAY_OF_WEEK);
    }

    //一年后的今天
    public void test2(){
    calendar.add(Calendar.YEAR, 1);

    //获取年
    int year = calendar.get(Calendar.YEAR);
    //获取月
    int month = calendar.get(Calendar.MONTH) + 1;
    //获取日
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    }

    //获取任意一个月的最后一天
    public void test3(){
    //假设求6月的最后一天
    int currentMonth = 6;
    //先求出7月份的第一天
    calendar.set(calendar.get(Calendar.YEAR), currentMonth, 1);
    calendar.add(Calenadr.DATA, -1);

    //获取日
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    }

    //设置日期
    public void test4(){
    calendar.set(Calendar.YEAR, 2000);
    System.out.println("现在是" + calendar.get(Calendar.YEAR) + "年");

    calendar.set(2018, 1, 2);
    //获取年
    int year = calendar.get(Calendar.YEAR);
    //获取月
    int month = calendar.get(Calendar.MONTH) + 1;
    //获取日
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    }

    public void test5(){
    calendar.set(2018, 7, 8);
    calendar.add(Calendar.DAY_OF_MONTH, -8);
    //获取年
    int year = calendar.get(Calendar.YEAR);
    //获取月
    int month = calendar.get(Calendar.MONTH) + 1;
    //获取日
    int day = calendar.get(Calendar.DAY_OF_MONTH);

    System.out.println("2018.7.8,用add减少8天,现在是" + year + "." + month + "." + day);
    //2018.7.31

    calendar.set(2018, 7, 8);
    calendar.roll(Calendar.DAY_OF_MONTH, -8);

    //获取年
    int year = calendar.get(Calendar.YEAR);
    //获取月
    int month = calendar.get(Calendar.MONTH) + 1;
    //获取日
    int day = calendar.get(Calendar.DAY_OF_MONTH);
    System.out.println("2018.7.8,用roll减少8天,现在是" + year + "." + month + "." + day);
    //2018.8.31只改变天数,不改变月份
    }

roll函数做加减法,只影响当前字段,不影响进位。add函数则会影响进位。

  • Java 8 退出新的时间API
    java.time包
    旧的设计不好(重名的类、线程不安全等)
    新版本优点:不变性,在多线程环境下;遵循设计模式,设计得更好,可扩展性强
  • Java 8 时间包概述
    java.time包:新的Java日期/时间API的基础包
    主要类:LocalDate日期类
    LocalTime时间类(时分秒-纳秒)
    LocalDateTime:LocalDate + LocalTime
    Instant时间戳
    java.time.chrono包:为非ISO的日历系统定义了一些泛化的API
    java.time.format包:格式化和解析日期时间对象的类
    java.time.temporal包:包含一些时态对象,可以用其找出关于日期/时间对象的某个特定日期或时间
    java.time.zone包:包含支持不同时区以及相关规则的类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    //当前时间
    LocalDate today = LocalDate.now();
    //根据指定时间创建LocalDate
    LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
    //给定错误时间参数,将报异常java.time.DateTimeException
    //LocalDate firstDay_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
    //可更改时区
    LocalDate todayBeijing = LocalDate.now(ZoneId.of("Asia/Shanghai"));
    //从纪元日01/01/1970开始365天
    LocalDate dateFromBase = LocalDate.ofEpochDay(365);
    //2014年的第100天
    LocalDate hundredDay2014 = LocalDate.ofYearDay(2014, 100);

    //当前时间 时分秒 纳秒
    LocalTime time = LocalTime.now();
    //根据时分秒
    LocalTime specificTime = LocalTime.of(12, 20, 25, 40);
    //错误的时间参数 将报DateTimeException
    //LocalTime invalidTime = LocalTime.of(25,20);
    //上海时间
    LocalTime timeSH = LocalTime.mow(ZoneId.of("Asia/Shanghai"));
    //一天当中第几秒
    LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);

    //当前时间戳
    Instant timestamp = Instant.now();
    //从毫秒数来创建时间戳
    Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());

    Date date = Date.from(timestamp);

    LocalDate today = LocalDate.now();
    //判断是否是闰年
    System.out.println("Year " + today.getYear() + "is Leap Year" + today.isLeapYear());

    //今天和01/01/2015比较
    System.out.println("Today is before 01/01/2015" + today.isBefore(LocalDate.of(2015,1,1)));
    //当前时分秒
    System.out.println("Current Time =" + today.atTime(LocalTime.now()));

    //加减时间
    System.out.println("10 days after today will be " + today.plusDays(10));
    System.out.println("3 weeks after today will be " + today.plusWeeks(3));
    System.out.println("20 months after today will be " + today.plusMonths(20));
    System.out.println("10 days after today will be " + today.minusDays(10));
    System.out.println("3 weeks after today will be " + today.minusWeeks(3));
    System.out.println("3 weeks after today will be " + today.minusMonths(20));

    //调整时间
    System.out.println("First date of this month = " + today.with(TemporalAdjusters.firstDayOfMonth()));
    LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
    System.out.println("Last date of this year = " + lastDayOfYear);

    //时间段计算
    Period period = today.until(lastDayOfYear);
    System.out.println("Period Format = " + period);
    System.out.println("Months remaining in the year = " + period.getMonths());

格式化(Format)相关类

  • java.text包java.text.Format的子类
    NumberFormat:数字格式化,抽象类
    – DecimalFormat 工厂模式
    例如:将1234567格式化输出为1,234,567

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    DecimalFormat df1,df2;
    //整数部分为0,#认为整数不存在,可不写;0认为没有,但至少写一位,写0
    df1 = new DecimalFormat("#.00");
    df2 = new DecimalFormat("0.00");

    System.out.println(df1.format(0.1));//.10
    System.out.println(df2.format(0.1));//0.10

    df1 = new DecimalFormat("0.00");
    df2 = new DecimalFormat("0.##");

    System.out.println(df1.format(0.1));//0.10
    System.out.println(df2.format(0.1));//0.1

    df1 = new DecimalFormat("0.00");
    df2 = new DecimalFormat("#.00");

    System.out.println(df1.format(2));//2.00
    System.out.println(df2.format(2));//2.00

    System.out.println(df1.format(20));//20.00
    System.out.println(df2.format(20));//20.00

    System.out.println(df1.format(200));//200.00
    System.out.println(df2.format(200));//200.00

    MessageFormat:字符串格式化
    支持多个参数-值对位复制文本S
    支持变量的自定义格式
    例如将“Hello {1}”根据变量值格式化为Hello World

    1
    2
    3
    4
    5
    6
    7
    String message = "{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}{16}";
    object[] array = new Object[]{"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q"};
    String value = MessageFormat.format(message, array);
    System.out.println(value);
    message = "oh, {0,number,#.##} is a good number";
    array = new Object[]{new Double(3.1415)};
    value = MessageFormat.format(message, array);

输出

1
2
ABCDEFGHIJKLMNOPQ
oh, 3.14 is a good number

DateFormat:日期/时间格式化,抽象类
– SimpleDateFormat 工厂模式
parse:将字符串格式化为时间对象
format:将时间对象格式化为字符串
如将当前时间转为YYYY-MM-DD HH24:MI:SS输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String strDate = "2008-10-19 10:11:30.345";
//准备第一个模板,从字符串中提取出日期数字
String pat1 = "yyyy-MM-dd HH:mm:ss.SSS";
//准备第二个模板,将提取后的日期数字变为指定的格式
String pat2 = "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒";
SimpleDateFormat sdf1 = new SimpleDateFormat(pat1);
SimpleDateFormat sdf2 = new SimpleDateFormat(pat2);
Date d = null;
try{
d = sdf1.parse(strDate);//将给定的字符串中的日期提取出来
}catch(Exception e){//如果提供的字符串格式有错误,则进行异常处理
e.printStackTrace();//打印异常信息
}
System.out.println(sdf2.format(d));//将日期变为新的格式

  • java.time.format包下
    java.time.format.DateFormatter:时间格式化
    JDK 8 发布,线程安全(vs SimpleDateFormat 线程不安全)
    ofPattern:设定时间格式
    parse:将字符串格式化为时间对象
    format:将时间对象格式化为字符串
    如将当前时间转为YYYY-MM-DD HH24:MI:SS输出
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //将字符串转化为时间
    String dateStr = "2020年1月1日";
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
    LocalDate date = LocalDate.parse(dateStr, formatter);
    System.out.println(date.getYear() + "-" + date.getMonthValue() + "-" + date.getDayOfMonth());

    //将日期转换为字符串输出
    LocalDateTime now = LocalDateTime.now();
    DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm:ss");
    String nowStr = now.format(format);
    System.out.println(nowStr);

LocalDate类的月份是1-12,不用加1;Calendar类的月份是0-11,需要加1。

异常和异常处理

异常

Java异常分成Exception(程序相关)和Error(系统相关)。

异常分类
异常分类

Exception分为RuntimeException程序自身的错误(如5/0,空指针,数组越界)和非RuntimeException外界相关的错误(如打开一个不存在的文件,加载一个不存在的类)。
IOException是非RuntimeException的典型示例。
另一种分类方法:
Unchecked Exception:(编译器不会辅助检查的,需要管理员自己管的)异常,包括Error子类和RuntimeException子类。
非RuntimeException的Exception的子类:(编译器会辅助检查的)异常,checked exception。
注意:编译器会检查程序是否为checked exception配置了处理。若没有处理,会报错。

异常处理

  • try-catch-finally:一种保护代码正常运行的机制。
  • 异常结构
    try…catch(catch可以有多个,下同)
    try…catch…finally
    try…finally
  • try必须有,catch和finally至少要有一个
    try:正常业务逻辑代码
    catch:当try发生异常,将执行catch代码。若无异常,绕之。
    finally:当try或catch执行结束后,必须要执行finally。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    try
    {
    int a = 5/2;
    System.out.println(a);
    }
    catch(Exception ex)
    {
    ex.printStackTrace();
    }
    finally
    {
    System.out.println("1 over");
    }


    try
    {
    int a = 5/0;
    System.out.println(a);
    }
    catch(Exception ex)
    {
    ex.printStackTrace();
    }
    finally
    {
    System.out.println("2 over");
    }

    try
    {
    int a = 5/0;
    System.out.println(a);
    }
    catch(Exception ex)
    {
    ex.printStackTrace();
    int a = 5/0;
    }
    finally
    {
    System.out.println("3 over");
    }

3中,第一个异常执行完finally后再执行第二个异常。(catch内部再次发生异常也不影响finally的正常运行。)
进入catch块后,并不会返回到try发生异常的位置,也不会执行后续的catch块,一个异常只能进入一个catch块。
一般将小异常(具体的异常子类,如ArithmeticException)写在前面,大(宽泛)的异常(如Exception)写在末尾。
允许包含:

1
2
3
4
5
6
7
8
9
try{
try-catch-finally结构
}
catch(){
try-catch-finally结构
}
finally{
try-catch-finally结构
}

  • 方法存在可能异常的语句,但不处理,可使用throws来声明异常。
  • 调用带有throws异常(checked exception)的方法,要么处理这些异常,或者再次向外throws,直到main函数为止。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class ThrowsDemo
    {
    public static void main(String [] args)
    {
    try
    {
    int result = new Test().divide(3,1);
    System.out.println("1 result:" + result);
    }
    catch(ArithmeticException ex)
    {
    ex.printStackTrace();
    }
    int result = new Test().divide(3,0);
    System.out.println("2 result:" + result);
    }
    }
    class Test
    {
    //ArithmeticException is a RuntimeException, not checked exception
    public int divide(int x, int y) throws ArithmeticException
    {
    int result = x/y;
    return x/y;
    }
    }
  • 一个方法被覆盖,覆盖它的方法不限抛出相同的异常,或者异常的子类。

  • 如果父类的方法抛出多个异常,那么重写的子类方法必须抛出那些异常的子集,也就是不能抛出新的异常。
    Father.java
    1
    2
    3
    4
    5
    6
    public class Father{
    public void f1() throws ArithmeticException
    {

    }
    }

Son.java

1
2
3
public class Son extends Father{
public void f1() throws Exception//错误
}

自定义异常

  • 自定义异常需要继承Exception类或其子类。
    继承自Exception,就变成Checked Exception。
    继承自RuntimeException,就变成Unchecked Exception。
    (非RuntimeException,是Checked Exception,IDE辅助检查;RuntimeException,是Unchecked Exception,IDE不会辅助检查,是程序员自行处理。)
  • 自定义重点在构造函数
    调用父类Exception的message构造函数
    可自定义自己的成员变量
  • 在程序中采用throw主动抛出异常
    MyException.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public class MyException extends Exception{
    private String returnCode;//异常对应的返回码
    private String returnMsg;//异常对应的描述信息

    public MyException(){
    super();
    }

    public MyException(String returnMsg){
    super(returnMsg);
    this.returnMsg = returnMsg;
    }

    public MyException(String returnCode, String returnMsg){
    super(returnMsg);
    this.returnCode = returnCode;
    this.returnMsg = returnMsg;
    }

    public String getReturnCode(){
    return returnCode;
    }

    public String getReturnMsg(){
    return returnMsg;
    }
    }

在方法内部程序中,抛出异常采用throw关键字;在方法头部声明中,声明异常采用throws关键字。
MyExceptionTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyExceptionTest {
public static void testException() throws MyException {
throw new MyException("10001", "The reason of myException");
}

public static void main(String[] args) {
//MyExceptionTest.testException();注释1 要报错需注释掉下方代码
try {
MyExceptionTest.testException();
} catch (MyException e) {
e.printStackTrace();
System.out.println("returnCode:" + e.getReturnCode());
System.out.println("returnCode:" + e.getReturnMsg());
}
}
}

注释1:main函数里调用了一个声明MyException异常的方法,但没处理。因为MyException继承Exception,是属于非RuntimeException,也就是Checked Exception,因此编译器会检查到程序没处理而报错。解法:1)采用try-catch-finally结构 2)main函数也throws MyException。
DivideByMinusException.java

1
2
3
4
5
6
7
8
9
10
11
12
public class DivideByMinusException extends Exception {
int divisor;
public DivideByMinusException(String msg, int divisor)
{
super(msg);
this.divisor = divisor;
}
public int getDevisor()
{
return this.getDevisor();
}
}

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class Student {
public int divide(int x, int y)
{
return x/y;
}
public static void main(String[] args) throws DivideByMinusException {
Student newton = new Student();
//newton.divide2(5,0);
newton.divide5(5,-2);
}

public int divide2(int x,int y)
{
int result;
try
{
result = x/y;
System.out.println("result is " + result);
}
catch(ArithmeticException ex)
{
System.out.println(ex.getMessage());
return 0;
}
catch(Exception ex)
{
ex.printStackTrace();
return 0;
}
}

public int divide3(int x,int y) throws ArithmeticException//unchecked exception
{
return x/y;
}

public int divide4(int x, int y)
{
// try
// {
// return divide3(x,y);
// }
// catch(ArithmeticException ex)
// {
// ex.printStackTrace();
// return 0;
// }
return divide3(x,y);//尽管divide3报告异常,但divide4无需处理。
//若调用divide5(x,y);就需要做try-catch处理。
}

public int divide5(int x,int y) throws DivideByMinusException
{
try
{
if(y < 0)
{
throw new DivideByMinusException("The divisor is negative", y);
}
return divide3(x,y);
}
catch(ArithmeticException ex)
{
ex.printStackTrace();
return 0;
}
}
}

对于CheckedException,编译器要求调用者处理,要么采用try-catch-finally处理,要么采用throws声明异常。

数据结构

数组

  • 数组是一个存放多个数据的容器
    数据是同一种类型
    所有数据是线性规则排列
    可通过位置索引来快速定位访问数据
    需明确容器的长度

  • Java数组定义和初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int a[];//还没有new操作,实际上是null,也不知道内存位置
    int[] b;
    int[] c = new int[2];//c有2个元素,都是0
    c[0] = 1, c[1] = 2;//初始化

    int d[] = new int[]{1,2,3};//同时定义和初始化
    int d1[] = {1,2,3};//同时定义和初始化

    //声明变量时没有分配内存,不需要指定大小,以下是错误的
    //int a[2];
    //int[2] b;
    //int[2] c = new int[2];
    //int d[2] = new int[2];
  • 数组索引
    数组的length属性标识数组的长度
    从0开始,到length-1

    1
    int[] a = new int[5];//a[0]~a[4],a.length是5

数组不能越界访问,否则会报ArrayIndexOutOfBoundsException异常

  • 数组遍历:两种方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //需控制索引位置
    for(int i = 0; i < d.length; i++)
    {
    System.out.println(d[i]);
    }

    //无需控制索引位置
    for(int e : d)
    {
    System.out.println(e);
    }
  • 多维数组
    数组的数组
    存储按照行存储原则

    1
    2
    3
    4
    5
    6
    7
    8
    //规则数组
    int a[][] = new int[2][3];
    //不规则数组
    int b[][];
    b = new int[3][];
    b[0] = new int[3];
    b[1] = new int[4];
    b[2] = new int[5];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int k = 0;
for(int i = 0; i < a.length; i++)
{
for(int j = 0; j < a[i].length; j++)
{
a[i][j] = ++k;
}
}

for(int[] items : a)
{
for(int item : items)
{
System.out.println(item + ", ");
}
System.out.println();
}

JCF

Java Collection Framework

  • 容器:能够存放数据的空间结构
    数组/多维数组,只能线性存放
    列表/散列集/树/…
  • 容器框架:为表示和操作容器而规定的一种标准体系结构
    对外的接口:容器中所能存放的抽象数据类型
    接口的实现:可复用的数据结构
    算法:对数据的查找和排序
  • 容器框架优点:提高数据存取效率,避免程序员重复劳动
  • C++的STL, Java的JCF

  • Java 1.1和以前的数据结构
    Vector, Stack, Hashtable, Enumeration等

  • Java 1.2和以后,JCF集合框架
    功能更强大,易于学习
    接口和实现分离,多种设计模式更灵活
    泛型设计

主要由列表(List)、散列集合(Set)、映射(Map)组成。(还有Arrays和Collection工具类)
Dictionary和Hashtable属于JDK1.1的类,已经较少使用了。

  • 早期接口Enumeration
  • JCF的集合接口是Collection
    add, contains, remove, size
    iterator
  • JCF的迭代器接口Iterator
    hasNext, next, remove

  • JCF主要的数据结构实现类
    列表(List, ArrayList, LinkedList)
    集合(Set, HashSet, TreeSet, LinkedHashSet)
    映射(Map, HashMap, TreeMap, LinkedHashMap)

  • JCF主要的算法类
    Arrays: 对数组进行查找和排序等操作
    Collections: 对Collection及其子类进行排序和查找操作

List

  • List 列表
    有序的Collection
    允许重复元素
    {1, 2, 3, {1, 2}, 1, 3}
  • List主要实现
    ArrayList(非同步)
    LinkedList(非同步)
    Vector(同步)

  • ArrayList
    以数组实现的列表,不支持同步
    利用索引位置可以快速定位访问
    不适合指定位置的插入、删除操作
    适合变动不大,主要用于查询的数据
    和Java数组相比,其容量是可动态调整的//Java数组是静态的,长度确定不可修改
    ArrayList在元素填满容器时会自动扩充容器大小的50%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import java.util.ArrayList;
//Vector几乎和ArrayList一样,除了Vector本身是同步的

public class ArrayListTest{
public static void main(String[] a){
ArrayList<Integer> al = new ArrayList<Integer>();//ArrayList<Integer> al 是泛型表示,意思是al这个数据结构里只能容纳Integer的对象,其它对象无法放入
al.add(1);//ArrayList只能装对象,当add(1)时,会自动将普通int变量1自动装箱为Integer(1)的对象,然后放入ArrayList容器中
al.add(2);
al.add(3);
al.add(4);
al.add(new Integer(6));

System.out.println(al.get(3));
al.remove(3);//删除第4个元素,后面元素往前挪
al.add(3, 0);//将0插入到第3个元素,后面元素往后挪

//遍历方法
ArrayList<Integer> as = new ArrayList<Integer>(100000);
for(int i = 0; i < 100000; i++)
{
as.add(i);
}
traverseByIterator(as);
traverseByIndex(as);
traverseByFor(as);
}
//迭代器遍历
public static void traverseByIterator(ArrayList<Integer> al)
{
long startTime = System.namoTime();
Iterator<Integer> iter1 = al.iterator();
while(iter1.hasNext()){
iter1.next();
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
//随机索引值遍历
public static void traverseByIndex(ArrayList<Integer> al)
{
long startTime = System.namoTime();
for(int i = 0; i< al.size(); i++)
{
al.get(i);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
//for循环遍历
public static void traverseByFor(ArrayList<Integer> al)
{
long startTime = System.namoTime();
for(Integer item : al)
{
;
}
long endTime = System.namoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}

结果表明:for循环遍历最快,随机索引值遍历和for差距不大,迭代器最慢。

  • LinkedList
    以双向链表实现的列表,不支持同步
    可被当作堆栈、队列和双端队列进行操作
    顺序访问高效、随机访问较差,中间插入和删除高效
    适用于经常变化的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import java.util.ArrayList;


public class LinkedListTest{
public static void main(String[] a){
LinkedList<Integer> ll = new LinkedList<Integer>();
ll.add(1);
ll.add(2);
ll.add(3);
ll.add(4);
System.out.println(ll.size());
ll.addFirst(0);//在头部增加0
ll.add(3,5);//将5插入到第四个元素,四及后续元素往后挪
ll.remove(3);//将第四个元素删除

LinkedList<Integer> list = new LinkedList<Integer>();
for(int i = 0; i < 100000; i++)
{
list.add(i);
}
traverseByIterator(list);
traverseByIndex(list);
traverseByFor(list);
}
//迭代器遍历
public static void traverseByIterator(LinkedList<Integer> list)
{
long startTime = System.namoTime();
Iterator<Integer> iter1 = list.iterator();
while(iter1.hasNext()){
iter1.next();
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
//随机索引值遍历
public static void traverseByIndex(LinkedList<Integer> list)
{
long startTime = System.namoTime();
for(int i = 0; i< list.size(); i++)
{
list.get(i);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
//for循环遍历
public static void traverseByFor(LinkedList<Integer> list)
{
long startTime = System.namoTime();
for(Integer item : list)
{
;
}
long endTime = System.namoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}

结果:for循环最快,迭代器稍微慢一些,随机索引特别慢。

性能比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.util.ArrayList;

public class ListCompareTest {
public static void main(String[] args) {
int times = 10 * 1000;

ArrayList<Integer> arrayList = new ArrayList<Integer>();
LinkedList<Integer> linkedList = new LinkedList<Integer>();

//ArrayList add
long startTime = System.nanoTime();

for(int i = 0; i < times; i++) {
arrayList.add(0, i);//add到头部
}
long endTime = System.namoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");

//LinkedList add
startTime = System.nanoTime();

for(int i = 0; i < times; i++) {
linkedList.add(0, i);
}
endTime = System.namoTime();
duration = endTime - startTime;
System.out.println(duration + "纳秒");

//ArrayList get
startTime = System.nanoTime();

for(int i = 0; i < times; i++) {
arrayList.get(i);
}
endTime = System.namoTime();
duration = endTime - startTime;
System.out.println(duration + "纳秒");

//LinkedList get
startTime = System.nanoTime();

for(int i = 0; i < times; i++) {
linkedList.get(i);
}
endTime = System.namoTime();
duration = endTime - startTime;
System.out.println(duration + "纳秒");

//ArrayList remove
startTime = System.nanoTime();

for(int i = 0; i < times; i++) {
arrayList.remove(0);
}
endTime = System.namoTime();
duration = endTime - startTime;
System.out.println(duration + "纳秒");

//LinkedList remove
startTime = System.nanoTime();

for(int i = 0; i < times; i++) {
linkedList.remove(0);
}
endTime = System.namoTime();
duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}

结果:add ArrayList时间远大于LinkedList
get ArrayList时间远小于LinkedList
remove ArrayList时间远大于LinkedList

结论:ArrayList适用于较多查询的(静态)数据,LinkedList适用于频繁增删的数据。

  • Vector(同步)
    和ArrayList类似,可变数组实现的列表
    Vector同步,适合在多线程下使用
    原先不属于JCF框架,属于Java最早的数据结构,性能较差
    从JDK 1.2开始,Vector被重写,并纳入到JCF
    官方文档建议在非同步情况下,优先采用ArrayList
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    import java.util.ArrayList;

    public class VectorTest {
    public static void main(String[] args) {
    Vector<Integer> v = new Vector<Integer>();
    v.add(1);
    v.add(2);
    v.add(3);
    v.remove(2);
    v.add(1, 5);
    System.out.println(v.size());

    //遍历方法

    Vector<Integer> v2 = new Vector<Integer>(100000);
    for(int i = 0; i < 100000; i++) {
    v2.add(i);
    }
    traverseByIterator(v2);
    traverseByIndex(v2);
    traverseByFor(v2);
    traverseByEnumeration(v2);
    }

    //迭代器遍历
    public static void traverseByIterator(Vector<Integer> v)
    {
    long startTime = System.namoTime();
    Iterator<Integer> iter1 = v.iterator();
    while(iter1.hasNext()){
    iter1.next();
    }
    long endTime = System.nanoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }
    //随机索引值遍历
    public static void traverseByIndex(Vector<Integer> v)
    {
    long startTime = System.namoTime();
    for(int i = 0; i< v.size(); i++)
    {
    v.get(i);
    }
    long endTime = System.nanoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }
    //for循环遍历
    public static void traverseByFor(Vector<Integer> v)
    {
    long startTime = System.namoTime();
    for(Integer item : v)
    {
    ;
    }
    long endTime = System.namoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }
    //Enumeration遍历
    public static void traverseByEnumeration(Vector<Integer> v)
    {
    long startTime = System.namoTime();
    for(Enumeration<Integer> enu = v.elements(); enu.hasMoreElements();) {
    enu.nextElement();
    }
    long endTime = System.namoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }
    }

结果:for循环是最快的,位置索引第二名,Enumeration第三,迭代器最慢。

Set

确定性:对任意对象都能判定其是否属于某一个集合
互异性:集合内每个元素都是不相同的,内容互异
无序性:集合内的顺序无关
集合接口Set

  • HashSet 基于散列函数的集合,无序,不支持同步
    基于HashMap实现的,可以容纳null元素,不支持同步
    但以下方法可以实现同步
    1
    Set s = Collections.synchronizedSet(new HashSet(...));

add 添加一个元素
clear 清除整个HashSet
contains 判定是否包含一个元素
remove 删除一个元素 size 大小
retainAll 计算两个集合交集

  • TreeSet 基于树结构的集合,可排序的,不支持同步
  • LinkedHashSet 基于散列函数和双向链表的集合,可排序的,不支持同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

public class HashSetTest {
public static void main(String[] args) {
HashSet<Integer> hs = new HashSet<Integer>();
hs.add(null);
hs.add(1);
hs.add(2);
hs.add(3);
hs.add(40000);
hs.add(5000000);
hs.add(3); //3 重复
hs.add(null); //null重复
System.out.println(hs.size()); //6
if(!hs.contains(6))
{
hs.add(6);
}
System.out.println(hs.size()); //7
hs.remove(4);
System.out.println(hs.size()); //6
//hs.clear();
//System.out.println(hs.size()); //0

//or循环遍历
for(Integer item : hs)
{
System.out.println(item);
}

//测试集合交集
HashSet<String> set1 = new HashSet<String>();
HashSet<String> set2 = new HashSet<String>();

set1.add("a");
set1.add("b");
set1.add("c");

set2.add("c");
set2.add("d");
set2.add("e");

//交集
set1.retainAll(set2);
System.out.println("交集是 "+set1);

//测试遍历方法速度
HashSet<Integer> hs2 = new HashSet<Integer>();
for(int i=0;i<100000;i++) {
hs2.add(i);
}
traverseByIterator(hs2);
traverseByFor(hs2);
}

public static void traverseByIterator(HashSet<Integer> hs)
{
long startTime = System.nanoTime();
//迭代器遍历
Iterator<Integer> iter1 = hs.iterator();
while(iter1.hasNext()){
iter1.next();
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
public static void traverseByFor(HashSet<Integer> hs)
{
long startTime = System.nanoTime();
//for循环遍历
for(Integer item : hs)
{
;
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}

迭代器慢于for循环。

  • LinkedHashSet
    继承HashSet,也是基于HashMap实现的,可容纳null元素
    不支持同步

    1
    Set s = Collections.synchronizedSet(new LinkedHashSet(...));
  • 方法和HashSet基本一致
    add,clear,contains,remove,size
    通过一个双向链表维护插入顺序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.LinkedHashSet;

    public class LinkedHashSetTest {
    public static void main(String[] args) {
    LinkedHashSet<Integer> lhs = new LinkedHashSet<Integer>();
    lhs.add(null);
    lhs.add(1000);
    lhs.add(20);
    lhs.add(3);
    lhs.add(40000);
    lhs.add(5000000);
    lhs.add(3); //3 重复
    lhs.add(null); //null 重复
    System.out.println(lhs.size()); //6
    if(!lhs.contains(6))
    {
    lhs.add(6);
    }
    System.out.println(lhs.size()); //7
    lhs.remove(4);
    System.out.println(lhs.size()); //6
    //lhs.clear();
    //System.out.println(lhs.size()); //0

    //for循环遍历
    for(Integer item : lhs)
    {
    System.out.println(item);
    }

    LinkedHashSet<Integer> lhs2 = new LinkedHashSet<Integer>();
    for(int i=0;i<100000;i++)
    {
    lhs2.add(i);
    }
    traverseByIterator(lhs2);
    traverseByFor(lhs2);

    }

    public static void traverseByIterator(LinkedHashSet<Integer> hs)
    {
    long startTime = System.nanoTime();
    //迭代器遍历
    Iterator<Integer> iter1 = hs.iterator();
    while(iter1.hasNext()){
    iter1.next();
    }
    long endTime = System.nanoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }
    public static void traverseByFor(LinkedHashSet<Integer> hs)
    {
    long startTime = System.nanoTime();
    //for循环遍历
    for(Integer item : hs)
    {
    ;
    }
    long endTime = System.nanoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }
    }

LinkedHashSet是保留顺序的,其遍历顺序和插入顺序一致;而HashSet没有保留顺序,其遍历顺序无序。

  • TreeSet
    基于TreeMap实现的,不可以容纳null元素,不支持同步
    1
    SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));

add 添加一个元素
clear 清除整个TreeSet
contains 判定是否包含一个元素
remove 删除一个元素
size 大小
根据compareTo方法或指定Comparator排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.TreeSet;

public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Integer> ts = new TreeSet<Integer>();
// ts.add(null); 错误,不支持null
ts.add(1000);
ts.add(20);
ts.add(3);
ts.add(40000);
ts.add(5000000);
ts.add(3); //3 重复
System.out.println(ts.size()); //5
if(!ts.contains(6))
{
ts.add(6);
}
System.out.println(ts.size()); //6
ts.remove(4);
System.out.println(ts.size()); //5
//lhs.clear();
//System.out.println(lhs.size()); //0

//for循环遍历
for(Integer item : ts)
{
System.out.println(item);
}

TreeSet<Integer> ts2 = new TreeSet<Integer>();
for(int i=0;i<100000;i++)
{
ts2.add(i);
}
traverseByIterator(ts2);
traverseByFor(ts2);

}

public static void traverseByIterator(TreeSet<Integer> hs)
{
long startTime = System.nanoTime();
//迭代器遍历
Iterator<Integer> iter1 = hs.iterator();
while(iter1.hasNext()){
iter1.next();
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
public static void traverseByFor(TreeSet<Integer> hs)
{
long startTime = System.nanoTime();
//for循环遍历
for(Integer item : hs)
{
;
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}

}

HashSet是无序输出的
LinkedHashSet是按照插入的顺序进行遍历输出的
TreeSet是按照所存储对象大小升序输出的

  • HashSet, LinkedHashSet, TreeSet的元素都只能是对象
  • HashSet和LinkedHashSet判定元素重复的原则
    判定两个元素的hashCode返回值是否相同,不同则返回false
    若两者hashCode相同,判定equals方法,不同则返回false
    hashCode和equals方法是所有类都有的,因为Object类有
  • TreeSet判定元素重复的原则
    需要元素继承自Comparable接口
    比较两个元素的compareTo方法
    HashSetJudgeRuleTest.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import java.util.HashSet;


    public class HashSetJudgeRuleTest {

    public static void main(String[] args) {
    HashSet<Cat> hs = new HashSet<Cat>();
    hs.add(new Cat(1));
    hs.add(new Cat(2));
    hs.add(new Cat(3));
    hs.add(new Cat(3));
    System.out.println(hs.size()); //4
    }
    }

Cat.java

1
2
3
4
5
6
7
8
9
10

class Cat
{
private int size;

public Cat(int size)
{
this.size = size;
}
}

Dog.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

class Dog {
private int size;

public Dog(int s) {
size = s;
}
public int getSize() {
return size;
}

public boolean equals(Object obj2) {
System.out.println("Dog equals()~~~~~~~~~~~");
if(0==size - ((Dog) obj2).getSize()) {
return true;
} else {
return false;
}
}

public int hashCode() {
System.out.println("Dog hashCode()~~~~~~~~~~~");
return size;
}

public String toString() {
System.out.print("Dog toString()~~~~~~~~~~~");
return size + "";
}
}

Tiger.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public class Tiger implements Comparable{
private int size;

public Tiger(int s) {
size = s;
}

public int getSize() {
return size;
}

public int compareTo(Object o) {
System.out.println("Tiger compareTo()~~~~~~~~~~~");
return size - ((Tiger) o).getSize();
}
}

Tiger实现Comparable接口,所以必须实现compareTo方法来比较大小。
compareTo方法具体规则如下:

1
int a = obj1.compareTo(obj2);

若a>0,则obj1>obj2;
若a==0,则obj1==obj2;
若a<0,则obj1<obj2。

HashSet的元素判定规则只和hashCode、equals这2个方法有关,和compareTo方法无关。

Java的四个重要接口:
Comparable可比较的
Clonable可克隆的
Runnable可线程化的
Serializable可序列化的
ObjectHashSetTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.TreeSet;


public class ObjectHashSetTest {

public static void main(String[] args) {
System.out.println("==========Cat HashSet ==============");
HashSet<Cat> hs = new HashSet<Cat>();
hs.add(new Cat(2));
hs.add(new Cat(1));
hs.add(new Cat(3));
hs.add(new Cat(5));
hs.add(new Cat(4));
hs.add(new Cat(4));
System.out.println(hs.size()); //6

System.out.println("========================");
LinkedHashSet<Cat> lhs= new LinkedHashSet<Cat>();
lhs.add(new Cat(2));
lhs.add(new Cat(1));
lhs.add(new Cat(3));
lhs.add(new Cat(5));
lhs.add(new Cat(4));
lhs.add(new Cat(4));
System.out.println(lhs.size()); //6



System.out.println("==========Dog HashSet ==============");
HashSet<Dog> hs2 = new HashSet<Dog>();
hs2.add(new Dog(2));
hs2.add(new Dog(1));
hs2.add(new Dog(3));
hs2.add(new Dog(5));
hs2.add(new Dog(4));
hs2.add(new Dog(4));
System.out.println(hs2.size()); //5

System.out.println("========================");
LinkedHashSet<Dog> lhs2= new LinkedHashSet<Dog>();
lhs2.add(new Dog(2));
lhs2.add(new Dog(1));
lhs2.add(new Dog(3));
lhs2.add(new Dog(5));
lhs2.add(new Dog(4));
lhs2.add(new Dog(4));
System.out.println(lhs2.size()); //5


System.out.println("==========Tiger HashSet ==============");
HashSet<Tiger> hs3 = new HashSet<Tiger>();
hs3.add(new Tiger(2));
hs3.add(new Tiger(1));
hs3.add(new Tiger(3));
hs3.add(new Tiger(5));
hs3.add(new Tiger(4));
hs3.add(new Tiger(4));
System.out.println(hs3.size()); //6

System.out.println("========================");
LinkedHashSet<Tiger> lhs3= new LinkedHashSet<Tiger>();
lhs3.add(new Tiger(2));
lhs3.add(new Tiger(1));
lhs3.add(new Tiger(3));
lhs3.add(new Tiger(5));
lhs3.add(new Tiger(4));
lhs3.add(new Tiger(4));
System.out.println(lhs3.size()); //6
}
}

Cat类本身没有hashCode(),而是继承Object类,而Object的hashCode()会返回对象信息和内存地址经过运算后的一个int值。两个不同的Cat(4)对象,它们的hashCode()返回值是不同的。
Dog类本身改写了hashCode()方法,其返回值是具体的size。所以两个不同Dog(4),它们的hashCode()返回值是相同的。

这三个方法三位一体:equals()是相同的;hashCode()是相同的;toString()也应该是相同的。

ObjectTreeSetTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.util.TreeSet;


public class ObjectTreeSetTest {

public static void main(String[] args) {
/*
System.out.println("==========Cat TreeSet ==============");
TreeSet<Cat> ts = new TreeSet<Cat>();
ts.add(new Cat(2));
ts.add(new Cat(1));
ts.add(new Cat(3));
ts.add(new Cat(5));
ts.add(new Cat(4));
ts.add(new Cat(4));
System.out.println(ts.size()); //5

System.out.println("==========Dog TreeSet ==============");


TreeSet<Dog> ts2 = new TreeSet<Dog>();
ts2.add(new Dog(2));
ts2.add(new Dog(1));
ts2.add(new Dog(3));
ts2.add(new Dog(5));
ts2.add(new Dog(4));
ts2.add(new Dog(4));
System.out.println(ts2.size()); //5
*/

//添加到TreeSet的,需要实现Comparable接口,即实现compareTo方法

System.out.println("==========Tiger TreeSet ==============");


TreeSet<Tiger> ts3 = new TreeSet<Tiger>();
ts3.add(new Tiger(2));
ts3.add(new Tiger(1));
ts3.add(new Tiger(3));
ts3.add(new Tiger(5));
ts3.add(new Tiger(4));
ts3.add(new Tiger(4));
System.out.println(ts3.size()); //5
}

}

Map

定义:两个集合之间的元素对应关系。
一个输入对应到一个输出。
{Key, Value} 键值对,K-V对

Hashtable 同步,慢,数据量小
HashMap 不支持同步,快,数据量大
Properties 同步,文件形式,数据量小

  • Hashtable
    K-V对,K和V都不允许为null
    同步,多线程安全
    无序的
    适合小数据量
    主要方法:clear, contains/containsValue, containsKey, get, put, remove, size
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    import java.util.Enumeration;
    import java.util.Hashtable;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Map.Entry;

    public class HashtableTest {

    public static void main(String[] args) {
    Hashtable<Integer,String> ht =new Hashtable<Integer,String>();
    //ht.put(1, null); 编译不报错 运行报错
    //ht.put(null,1); 编译报错
    ht.put(1000, "aaa");
    ht.put(2, "bbb");
    ht.put(30000, "ccc");
    System.out.println(ht.contains("aaa"));
    System.out.println(ht.containsValue("aaa"));
    System.out.println(ht.containsKey(30000));
    System.out.println(ht.get(30000));

    ht.put(30000, "ddd"); //更新覆盖ccc
    System.out.println(ht.get(30000));

    ht.remove(2);
    System.out.println("size: " + ht.size());

    ht.clear();
    System.out.println("size: " + ht.size());


    Hashtable<Integer,String> ht2 =new Hashtable<Integer,String>();
    for(int i=0;i<100000;i++)
    {
    ht2.put(i, "aaa");
    }
    traverseByEntry(ht2);
    traverseByKeySet(ht2);
    traverseByKeyEnumeration(ht2);
    }

    public static void traverseByEntry(Hashtable<Integer,String> ht)
    {
    long startTime = System.nanoTime();
    System.out.println("============Entry迭代器遍历==============");
    Integer key;
    String value;
    Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator();
    while(iter.hasNext()) {
    Map.Entry<Integer, String> entry = iter.next();
    // 获取key
    key = entry.getKey();
    // 获取value
    value = entry.getValue();
    //System.out.println("Key:" + key + ", Value:" + value);
    }
    long endTime = System.nanoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }


    public static void traverseByKeySet(Hashtable<Integer,String> ht)
    {
    long startTime = System.nanoTime();
    System.out.println("============KeySet迭代器遍历==============");
    Integer key;
    String value;
    Iterator<Integer> iter = ht.keySet().iterator();
    while(iter.hasNext()) {
    key = iter.next();
    // 获取value
    value = ht.get(key);
    //System.out.println("Key:" + key + ", Value:" + value);
    }
    long endTime = System.nanoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }


    public static void traverseByKeyEnumeration(Hashtable<Integer,String> ht)
    {
    long startTime = System.nanoTime();
    System.out.println("============KeyEnumeration迭代器遍历==============");
    Integer key;
    String value;
    Enumeration<Integer> keys = ht.keys();
    while(keys.hasMoreElements()) {
    key = keys.nextElement();
    // 获取value
    value = ht.get(key);
    //System.out.println("Key:" + key + ", Value:" + value);
    }
    long endTime = System.nanoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }
    }

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
true
true
true
ccc
ddd
size: 2
size: 0
============Entry迭代器遍历==============
11238700纳秒
============KeySet迭代器遍历==============
10546900纳秒
============KeyEnumeration迭代器遍历==============
11115100纳秒

  • HashMap
  • K-V对,K和V都运行为null
  • 不同步,多线程不安全
    Map m = Collections.synchronizedMap(new HashMap(…));
    无序的
    主要方法:clear, containsValue, containsKey, get, put, remove, size
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

public class HashMapTest {

public static void main(String[] args) {
HashMap<Integer,String> hm =new HashMap<Integer,String>();
hm.put(1, null);
hm.put(null, "abc");
hm.put(1000, "aaa");
hm.put(2, "bbb");
hm.put(30000, "ccc");
System.out.println(hm.containsValue("aaa"));
System.out.println(hm.containsKey(30000));
System.out.println(hm.get(30000));

hm.put(30000, "ddd"); //更新覆盖ccc
System.out.println(hm.get(30000));

hm.remove(2);
System.out.println("size: " + hm.size());

hm.clear();
System.out.println("size: " + hm.size());


HashMap<Integer,String> hm2 =new HashMap<Integer,String>();
for(int i=0;i<100000;i++)
{
hm2.put(i, "aaa");
}
traverseByEntry(hm2);
traverseByKeySet(hm2);
}

public static void traverseByEntry(HashMap<Integer,String> ht)
{
long startTime = System.nanoTime();
System.out.println("============Entry迭代器遍历==============");
Integer key;
String value;
Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry<Integer, String> entry = iter.next();
// 获取key
key = entry.getKey();
// 获取value
value = entry.getValue();
//System.out.println("Key:" + key + ", Value:" + value);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}


public static void traverseByKeySet(HashMap<Integer,String> ht)
{
long startTime = System.nanoTime();
System.out.println("============KeySet迭代器遍历==============");
Integer key;
String value;
Iterator<Integer> iter = ht.keySet().iterator();
while(iter.hasNext()) {
key = iter.next();
// 获取value
value = ht.get(key);
//System.out.println("Key:" + key + ", Value:" + value);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
true
true
ccc
ddd
size: 4
size: 0
============Entry迭代器遍历==============
13389400纳秒
============KeySet迭代器遍历==============
12616200纳秒

  • LinkedHashMap
    基于双向链表的维持插入顺序的HashMap
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    import java.util.LinkedHashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Map.Entry;

    public class LinkedHashMapTest {

    public static void main(String[] args) {
    LinkedHashMap<Integer,String> hm =new LinkedHashMap<Integer,String>();
    hm.put(1, null);
    hm.put(null, "abc");
    hm.put(1000, "aaa");
    hm.put(2, "bbb");
    hm.put(30000, "ccc");
    System.out.println(hm.containsValue("aaa"));
    System.out.println(hm.containsKey(30000));
    System.out.println(hm.get(30000));

    hm.put(30000, "ddd"); //更新覆盖ccc
    System.out.println(hm.get(30000));

    hm.remove(2);
    System.out.println("size: " + hm.size());

    //hm.clear();
    //System.out.println("size: " + hm.size());

    System.out.println("遍历开始==================");

    Integer key;
    String value;
    Iterator<Entry<Integer, String>> iter = hm.entrySet().iterator();
    while(iter.hasNext()) {
    Map.Entry<Integer, String> entry = iter.next();
    // 获取key
    key = entry.getKey();
    // 获取value
    value = entry.getValue();
    System.out.println("Key:" + key + ", Value:" + value);
    }
    System.out.println("遍历结束==================");

    LinkedHashMap<Integer,String> hm2 =new LinkedHashMap<Integer,String>();
    for(int i=0;i<100000;i++)
    {
    hm2.put(i, "aaa");
    }
    traverseByEntry(hm2);
    traverseByKeySet(hm2);
    }

    public static void traverseByEntry(LinkedHashMap<Integer,String> ht)
    {
    long startTime = System.nanoTime();
    System.out.println("============Entry迭代器遍历==============");
    Integer key;
    String value;
    Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator();
    while(iter.hasNext()) {
    Map.Entry<Integer, String> entry = iter.next();
    // 获取key
    key = entry.getKey();
    // 获取value
    value = entry.getValue();
    //System.out.println("Key:" + key + ", Value:" + value);
    }
    long endTime = System.nanoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }


    public static void traverseByKeySet(LinkedHashMap<Integer,String> ht)
    {
    long startTime = System.nanoTime();
    System.out.println("============KeySet迭代器遍历==============");
    Integer key;
    String value;
    Iterator<Integer> iter = ht.keySet().iterator();
    while(iter.hasNext()) {
    key = iter.next();
    // 获取value
    value = ht.get(key);
    //System.out.println("Key:" + key + ", Value:" + value);
    }
    long endTime = System.nanoTime();
    long duration = endTime - startTime;
    System.out.println(duration + "纳秒");
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
true
true
ccc
ddd
size: 4
遍历开始==================
Key:1, Value:null
Key:null, Value:abc
Key:1000, Value:aaa
Key:30000, Value:ddd
遍历结束==================
============Entry迭代器遍历==============
9191900纳秒
============KeySet迭代器遍历==============
9211800纳秒

HashMap遍历是无序的
LinkedHashMap遍历的顺序和它的插入的顺序保持一致
TreeMap遍历的顺序是按照大小或者compareTo方法规定的

  • TreeMap
    基于红黑树的Map,可以根据key的自然排序或者compareTo方法进行排序输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import java.util.TreeMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

public class TreeMapTest {

public static void main(String[] args) {
TreeMap<Integer,String> hm =new TreeMap<Integer,String>();
hm.put(1, null);
//hm.put(null, "abc"); 编译没错,运行报空指针异常
hm.put(1000, "aaa");
hm.put(2, "bbb");
hm.put(30000, "ccc");
System.out.println(hm.containsValue("aaa"));
System.out.println(hm.containsKey(30000));
System.out.println(hm.get(30000));

hm.put(30000, "ddd"); //更新覆盖ccc
System.out.println(hm.get(30000));

//hm.remove(2);
System.out.println("size: " + hm.size());

//hm.clear();
//System.out.println("size: " + hm.size());

System.out.println("遍历开始==================");

Integer key;
String value;
Iterator<Entry<Integer, String>> iter = hm.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry<Integer, String> entry = iter.next();
// 获取key
key = entry.getKey();
// 获取value
value = entry.getValue();
System.out.println("Key:" + key + ", Value:" + value);
}
System.out.println("遍历结束==================");

TreeMap<Integer,String> hm2 =new TreeMap<Integer,String>();
for(int i=0;i<100000;i++)
{
hm2.put(i, "aaa");
}
traverseByEntry(hm2);
traverseByKeySet(hm2);
}

public static void traverseByEntry(TreeMap<Integer,String> ht)
{
long startTime = System.nanoTime();
System.out.println("============Entry迭代器遍历==============");
Integer key;
String value;
Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry<Integer, String> entry = iter.next();
// 获取key
key = entry.getKey();
// 获取value
value = entry.getValue();
//System.out.println("Key:" + key + ", Value:" + value);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}


public static void traverseByKeySet(TreeMap<Integer,String> ht)
{
long startTime = System.nanoTime();
System.out.println("============KeySet迭代器遍历==============");
Integer key;
String value;
Iterator<Integer> iter = ht.keySet().iterator();
while(iter.hasNext()) {
key = iter.next();
// 获取value
value = ht.get(key);
//System.out.println("Key:" + key + ", Value:" + value);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
true
true
ccc
ddd
size: 4
遍历开始==================
Key:1, Value:null
Key:2, Value:bbb
Key:1000, Value:aaa
Key:30000, Value:ddd
遍历结束==================
============Entry迭代器遍历==============
12081400纳秒
============KeySet迭代器遍历==============
19865000纳秒
  • Properties
    继承于Hashtable
    可以将K-V对保存在文件中
    适用于数据量少的配置文件
    继承自Hashtable的方法: clear, contains/containsValue, containsKey, get, put, remove, size
    从文件加载的load方法,写入到文件中的store方法
    获取属性getProperty,设置属性setProperty
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.Enumeration;
    import java.util.Properties;

    //关于Properties类常用的操作
    public class PropertiesTest {
    //根据Key读取Value
    public static String GetValueByKey(String filePath, String key) {
    Properties pps = new Properties();
    try {
    InputStream in = new BufferedInputStream (new FileInputStream(filePath));
    pps.load(in); //所有的K-V对都加载了
    String value = pps.getProperty(key);
    //System.out.println(key + " = " + value);
    return value;

    }catch (IOException e) {
    e.printStackTrace();
    return null;
    }
    }

    //读取Properties的全部信息
    public static void GetAllProperties(String filePath) throws IOException {
    Properties pps = new Properties();
    InputStream in = new BufferedInputStream(new FileInputStream(filePath));
    pps.load(in); //所有的K-V对都加载了
    Enumeration en = pps.propertyNames(); //得到配置文件的名字

    while(en.hasMoreElements()) {
    String strKey = (String) en.nextElement();
    String strValue = pps.getProperty(strKey);
    //System.out.println(strKey + "=" + strValue);
    }

    }

    //写入Properties信息
    public static void WriteProperties (String filePath, String pKey, String pValue) throws IOException {
    File file = new File(filePath);
    if(!file.exists())
    {
    file.createNewFile();
    }
    Properties pps = new Properties();

    InputStream in = new FileInputStream(filePath);
    //从输入流中读取属性列表(键和元素对)
    pps.load(in);
    //调用 Hashtable 的方法 put。使用 getProperty 方法提供并行性。
    //强制要求为属性的键和值使用字符串。返回值是 Hashtable 调用 put 的结果。
    OutputStream out = new FileOutputStream(filePath);
    pps.setProperty(pKey, pValue);
    //以适合使用 load 方法加载到 Properties 表中的格式,
    //将此 Properties 表中的属性列表(键和元素对)写入输出流
    pps.store(out, "Update " + pKey + " name");
    out.close();
    }

    public static void main(String [] args) throws IOException{
    System.out.println("写入Test.properties================");
    WriteProperties("Test.properties","name", "12345");

    System.out.println("加载Test.properties================");
    GetAllProperties("Test.properties");

    System.out.println("从Test.properties加载================");
    String value = GetValueByKey("Test.properties", "name");
    System.out.println("name is " + value);
    }
    }

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
true
true
ccc
ddd
size: 4
遍历开始==================
Key:1, Value:null
Key:2, Value:bbb
Key:1000, Value:aaa
Key:30000, Value:ddd
遍历结束==================
============Entry迭代器遍历==============
13335400纳秒
============KeySet迭代器遍历==============
20651700纳秒

工具类

JCF中工具类
不存储数据,而是在数据容器上,实现高效操作
排序
搜索

  • Arrays类
    排序:对数组排序,sort/parallelSort
    查找:从数组中查找一个元素,binarySearch
    批量拷贝:从源数组批量复制元素到目标数组,copyOf。
    批量赋值:对数组进行批量赋值,fill。
    等价性比较:判定两个数组内容是否相同,equals。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    import java.util.Arrays;
    import java.util.Random;

    public class ArraysTest {
    public static void main(String[] args) {
    testSort();
    testSearch();
    testCopy();
    testFill();
    testEquality();
    }
    public static void testSort() {
    Random r = new Random();
    int[] a = new int[10];
    for(int i=0;i<a.length;i++) {
    a[i] = r.nextInt();
    }
    System.out.println("===============测试排序================");
    System.out.println("排序前");
    for(int i=0;i<a.length;i++) {
    System.out.print(a[i] + ",");
    }
    System.out.println();
    System.out.println("排序后");
    Arrays.sort(a);
    for(int i=0;i<a.length;i++) {
    System.out.print(a[i] + ",");
    }
    System.out.println();
    }

    public static void testSearch() {
    Random r = new Random();
    int[] a = new int[10];
    for(int i=0;i<a.length;i++)
    {
    a[i] = r.nextInt();
    }
    a[a.length-1] = 10000;
    System.out.println("===========测试查找============");
    System.out.println("10000 的位置是" + Arrays.binarySearch(a, 10000));
    }

    public static void testCopy() {
    Random r = new Random();
    int[] a = new int[10];
    for(int i=0;i<a.length;i++)
    {
    a[i] = r.nextInt();
    }
    int[] b = Arrays.copyOf(a, 5);
    System.out.println("===========测试拷贝前五个元素============");
    System.out.print("源数组:");
    for(int i=0;i<a.length;i++)
    {
    System.out.print(a[i] + ",");
    }
    System.out.println();
    System.out.print("目标数组:");
    for(int i=0;i<b.length;i++)
    {
    System.out.print(b[i] + ",");
    }
    System.out.println();
    }
    public static void testFill() {
    int[] a = new int[10];
    Arrays.fill(a, 100);
    Arrays.fill(a, 2, 8, 200);
    System.out.println("===========测试批量赋值============");
    System.out.print("数组赋值后:");
    for(int i=0;i<a.length;i++)
    {
    System.out.print(a[i] + ",");
    }
    System.out.println();
    }
    public static void testEquality() {
    int[] a = new int[10];
    Arrays.fill(a, 100);
    int[] b = new int[10];
    Arrays.fill(b, 100);
    System.out.println(Arrays.equals(a, b));
    b[9] = 200;
    System.out.println(Arrays.equals(a, b));
    }
    }
  • Collections类
    处理对象是Collection及其子类
    排序:对List进行排序,sort
    搜索:从List中搜索元素,binarySearch
    批量赋值:对List批量赋值,fill
    最大、最小:查找集合中最大/小值,max,min
    反序:将List反序排列,reverse

对象比较

  • 对象实现Comparable接口(需要修改对象类)
    compareTo方法
    “>” 返回1,”==”返回0,”<” 返回-1
    Arrays和Collections在进行对象sort时,自动调用该方法
  • 新建Comparator(适用于对象类不可更改的情况)
    compare方法
    “>” 返回1,”==”返回0,”<” 返回-1
    Comparator比较强将作为参数提交给工具类的sort方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    import java.util.Arrays;

    public class Person implements Comparable<Person> {
    String name;
    int age;

    public String getName() {
    return name;
    }

    public int getAge() {
    return age;
    }

    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }

    public int compareTo(Person another) {
    int i = 0;
    i = name.compareTo(another.name); // 使用字符串的比较
    if (i == 0) {
    // 如果名字一样,比较年龄, 返回比较年龄结果
    return age - another.age;
    } else {
    return i; // 名字不一样, 返回比较名字的结果.
    }
    }

    public static void main(String... a) {
    Person[] ps = new Person[3];
    ps[0] = new Person("Tom", 20);
    ps[1] = new Person("Mike", 18);
    ps[2] = new Person("Mike", 20);

    Arrays.sort(ps);
    for (Person p : ps) {
    System.out.println(p.getName() + "," + p.getAge());
    }
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public class Person2 {
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}

public Person2(String name, int age)
{
this.name = name;
this.age = age;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.Arrays;
import java.util.Comparator;

public class Person2Comparator implements Comparator<Person2> {
public int compare(Person2 one, Person2 another) {
int i = 0;
i = one.getName().compareTo(another.getName());
if (i == 0) {
// 如果名字一样,比较年龄,返回比较年龄结果
return one.getAge() - another.getAge();
} else {
return i; // 名字不一样, 返回比较名字的结果.
}
}

public static void main(String[] args) {
// TODO Auto-generated method stub
Person2[] ps = new Person2[3];
ps[0] = new Person2("Tom", 20);
ps[1] = new Person2("Mike", 18);
ps[2] = new Person2("Mike", 20);

Arrays.sort(ps, new Person2Comparator());
for (Person2 p : ps) {
System.out.println(p.getName() + "," + p.getAge());
}
}
}

运行结果

1
2
3
Mike,18
Mike,20
Tom,20

文件读写

文件系统及Java文件基本操作

文件系统是由OS(操作系统)管理的
文件系统和Java进程是平行的,是两套系统
文件系统是由文件夹和文件递归组合而成
文件目录分隔符
Linux/Unix 用/隔开
Windows用\隔开,涉及到转义,在程序中需用/或\代替
文件包括文件里面的内容和文件基本属性
文件基本属性:名称、大小、扩展名、修改时间等

文件类File
java.io.File是文件和目录的重要类(JDK6及以前是唯一)
目录也使用File类进行表示
File类与OS无关,但会受到OS的权限限制
常用方法
createNewFile,delete,exists, getAbsolutePath, getName, getParent,getPath, isDirectory, isFile, length, listFiles, mkdir, mkdirs
注意:File不涉及到具体的文件内容,只涉及属性