JAVA知识梳理

Java基础

浏览数:343

2019-6-15

由于最近一个项目需要,需要学习JAVA知识,学习的过程中做了一些笔记,主要以知识点记录为主,现在分享出来供参考.大部分内容是参考的自强学堂.这里做了归纳.
接口:

在JAVA中,接口可理解为对象间相互通信的协议,接口在继承中扮演着很重要的角色
接口只定义派生要用到的方法,但方法的具体实现完全取决于派生类

JAVA面向对象中支持的基本概念:

封装,继承,多态,抽象,类,对象,实例,方法,消息解析

类:

是一个模板,它描述一类对象的行为和状态,创建JAVA对象的模板

对象:

类的一个实例,有状态和行为

类变量:

声明在类中,方法体之外,但必须声明为static类型

构造方法:

在创建一个对象的时候,至少要调用一个构造方法,构造方法的名称必须与类同名,一个类可以有多个构造方法

创建对象:

需要三步:1.声明,声明一个对象,包括对象名称和类型
         2.实例化,使用关键字new来创建一个对象
         3.初始化,使用new创建对象时,会调用构造方法初始化对象

public class Puppy{
    public Puppy(String name){
        //这个构造器仅有一个参数:name
        System.out.println("Passed Name is :" + name);
    }
    public static void main(String[] args){
        Puppy myPuppy = new Puppy("zhangtong");
    }
}

源文件声明规则:

一个源文件中只能有一个public类
一个源文件可以有多个非public类
源文件的名称应该和public的类名保持一致.
如果一个类定义在某个包中,那么package语句应该放在源文件的首行
如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面
import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明

引用类型

引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象.
对象、数组都是引用数据类型
所有引用类型的默认值都是null
一个引用变量可以用来引用与任何与之兼容的类型

常量

是一个固定值,不需要计算,直接代表相应的值,不能改变
final double PI = 3.1415926;
常量名一般大写
字面量可以赋给任何内置类型的变量
byte a = 68;
char a = 'A';

字符串常量和字符常量都可以包括任何Unicode字符
char a = '\u0001';
String a = "\u0001";

转义字符

Java支持一些特殊的转义字符序列
\n 换行
\r 回车
\f 换页符
\b 退格
\s 空格
\t 制表符
\" 双引号
\' 单引号
\\反斜杠
\ddd 八进制字符
\uxxx 16进制unicode字符

Java支持的变量类型

局部变量, 成员变量, 类变量(静态变量)

局部变量声明在方法,构造方法或者语句块中
局部变量在方法,构造方法,或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁
访问修饰符不能用于局部变量
局部变量只能在声明它的方法,构造方法或者语句块中可见
局部变量在栈上分配
局部变量没有默认值,所以局部变量被声明后,必须经过初始化才可使用

实例变量声明在一个类中,但在方法,构造方法和语句块之外
当一个对象被实例化之后,每个实例变量的值就跟着确定
实例变量在对象创建的时候创建,在对象销毁的时候销毁
实例变量的值应该至少被一个方法,构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量的信息
访问修饰符可以修饰实例变量
实例变量对于类中的方法,构造方法或者语句块是可见的,一般情况下应该把实例变量设为私有.
实例变量具有默认值,数值类型的默认值是0,布尔变量的默认值是false,引用类型变量的默认值是null.

类变量以static关键字声明,但必须在构造方法和语句块之外.
无论一个类创建了多少个对象,类只拥有类变量的一份拷贝
静态变量除了被声明为常量外很少使用,常量是指声明为public/private, final 和 static类型的变量,常量初始化后不可改变
静态变量存储在静态存储区,经常被声明为常量
静态变量在程序开始时创建,在程序结束时销毁
与实例变量具有相似的可见性,但为了对类的使用者可见,大多数静态变量声明为public
静态变量可通过: ClassName.VariableName的方式访问
类变量被声明为public static final类型时,类变量名称必须使用大写字母.
如果静态变量不是public和final类型,其命名方式与实例变量以及局部变量的命名方式一致

访问控制修饰符

默认的,default,在同一包内可见,不使用任何修饰符
私有的, 以private修饰符指定,在同一类内可见
共有的, 以public修饰符指定,对所有的类可见
受保护的, 以protected修饰符指定,对 同一包内的类和所有子类 可见

接口里的变量都隐式声明为public static final,而接口里的方法默认情况下访问权限是public
类和接口不能被声明为private

private访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据
如果几个相互访问的public类分布在不同的包中,则需要导入相应public类所在的包,
由于类的继承性,类所有的公有方法和变量都能被其子类继承

接口的成员变量和方法不能声明为protected

非访问修饰符

static修饰符,用来创建方法和类变量
final修饰符,用来修饰类,方法和变量,final修饰的类不能被继承,修饰的方法不能被继承类重新定义,
修饰的变量为常量,是不可修改的.
abstract修饰符,用来创建抽象类和抽象方法
synchronized和volatile修饰符,主要用于线程的编程

static修饰符:
    静态变量:
        static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有
        一份拷贝.静态变量也被称为类变量.
    静态方法:
        static 关键字用来声明独立于对象的静态方法, 静态方法不能使用类的非静态变量.
    对类变量和方法的访问可以直接使用classname.variablename和classname.methodname的方式访问.
    
final修饰符:
    final变量能被显式地初始化且只能初始化一次,被声明为final的对象的引用不能指向不同的对象.
    但是final对象里的数据可以被改变.也就是说final对象的引用不能改变,但是里面的值可以改变
    
    final修饰符通常和static修饰符一起使用来创建类常量
    
    类中的final方法可以被子类继承,但是不能被子类修改
    声明final方法 的主要目的 是防止该方法的内容被修改.
    
    
abstract修饰符
    抽象类:
        不能用来实例化对象,声明抽象类的 唯一 目的是为了将来对该类进行扩充
        一个类不能同时被abstract和final修饰.如果一个类包含抽象方法,那么该类一定要声明为抽象类
        抽象类可以包含抽象方法和非抽象方法
    抽象方法:
        抽象方法是一种没有任何实现的方法,该方法的具体实现由子类提供.抽象方法不能同时被声明为final和static
        任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类
        
synchronized修饰符
    synchronized关键字声明的方法同一时间只能被一个线程访问.
    synchronized修饰符可以应用于四个访问修饰符.

transient修饰符
    序列化的对象包好被transient修饰的实例变量时,java虚拟机跳过该特定的变量
    该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型
    一般变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问
    
volatile修饰符
    volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值.
    当成员变量发生变化时,强迫线程将变化值回写到共享内存.这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值.

运算符

算数运算符,关系运算符,位运算符,逻辑运算符,赋值运算符,其它运算符
条件运算符(?:),也称为三元运算符
variable x = (expression) ? value if true : value if false
instanceOf运算符:该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)

Java Number类

当需要使用数字的时候,我们通常使用内置数据类型,如: byte,int,long,double等
然鹅,在实际开发中,我们经常会遇到需要使用对象,而不是内置数据类型的情形,
为了解决这个问题,Java语言为每一个内置数据类型提供了对应的包装类
所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类Number的子类
这种由 编译器 特别支持的包装称为 装箱, 所以当内置数据类型被当作对象使用的时候,
编译器会把内置类型装箱为包装类.相似的,编译器也可把一个对象拆箱为内置类型.
Number类属于java.lang包

Number类的成员方法
    xxxValue(): 将number对象转换为xxx数据类型的值并返回
    compareTo(): 将number对象与参数比较
    equals(): 判断number对象是否与参数相等
    valueOf(): 返回一个Integer对象指定的内置数据类型
    toString(): 以字符串形式返回值
    parseInt(): 将字符串解析为int类型
    abs(): 返回参数的绝对值
    ceil(): 对整形变量向左取整,返回类型为double型
    floor(): 对整型变量向右取整。返回类型为double类型
    rint(): 返回与参数最接近的整数。返回类型为double
    round(): 返回一个最接近的int、long型值
    min(): 返回两个参数中的最小值
    max(): 返回两个参数中的最大值
    exp(): 返回自然数底数e的参数次方
    log(): 返回参数的自然数底数的对数值
    pow(): 返回第一个参数的第二个参数次方
    sqrt(): 求参数的算术平方根
    sin(): 求指定double类型参数的正弦值
    cos(): 求指定double类型参数的余弦值
    tan(): 求指定double类型参数的正切值
    random(): 返回一个随机数
    

Java Character类

    使用字符时,我们通常使用的是内置数据类型cahr
    然后在实际开发中,我们经常会遇到需要使用对象,为了解决这个问题,Java语言为内置数据
    类型char提供了包装类Character类.
    
    Character类提供了一系列方法来操纵字符,可以使用Character的构造方法创建一个Character类对象
    Character ch = new Character('a');
    
Character类的成员方法
    isLetter(): 是否是一个字母
    isDigit(): 是否是一个数字字符
    isWhitespace(): 是否一个空格
    isUpperCase(): 是否是大写字母
    isLowerCase(): 是否是小写字母
    toUpperCase(): 指定字母的大写形式
    toLowerCase(): 指定字母的小写形式
    toString(): 返回字符的字符串形式,字符串的长度仅为1
    ...请参考java.lang.Character API规范
    

Java String类

在Java中字符串属于对象, Java提供了String类来创建和操作字符串
创建字符串:
    String类有11种构造方法,这些方法提供不同的参数来初始化字符串
    
    public class Main {

    public static void main(String[] args) {
        String greeting = "Hello world!";
        char[] helloArray = {'h', 'e', 'l', 'l', 'o', '.'};
        String helloString = new String(helloArray);
        System.out.println(helloString);
    }
}
注意: String类是不可改变的,所以一旦创建了String对象,那它的值就无法改变了.
    如果需要对字符串做很多修改,那么应该选择使用StringBuffer & StringBuilder类
    
字符串长度
    用于获取有关对象的信息的方法称为访问器方法
    String类的一个访问器方法是length()方法,它返回字符串对象包含的字符数
连接字符串
    String类提供了连接两个字符串的方法
        String s3 = string1.concat(string2);    //返回string1连接string2的新字符串
        也可对字符串常量使用concat()方法:
        String s4 = "My name is".concat("Zara");
        
        更常用的是使用'+'操作符来连接字符串
        String s5 = "Hello, " + " world" + "!";

创建格式化字符串
    String类使用静态方法format()返回一个String对象而不是PrintStream对象
    String类的静态方法format()能用来创建可复用的格式化字符串,而不仅仅是用于一次打印输出
    //例子:
    String fs;
    float floatVar = 9.8f;
    int intVar = 125;
    String stringVar = "Jimy";
    fs = String.format("The value of the float variable is " +
                        "%f, while the value of the integer" +
                        "variable is %d, and the string " +
                        "is %s", floatVar, intVar, stringVar);
    System.out.println(fs); //The value of the float variable is 9.800000, while the value of the integervariable is 125, and the string is Jimy

String 方法
    char charAt(int index): 返回指定索引处的 char 值
    int compareTo(Object o): 把这个字符串和另一个对象比较
    int compareTo(String anotherString): 按字典顺序比较两个字符串
    int compareToIgnoreCase(String str): 按字典顺序比较两个字符串,不考虑大小写
    String concat(String str): 将指定字符串连接到此字符串的结尾
    boolean contentEquals(StringBuffer sb): 当且仅当字符串与指定的StringButter有相同顺序的字符时候返回真
    static String copyValueOf(char[] data): 返回指定数组中表示该字符序列的 String
    boolean endsWith(String suffix): 测试此字符串是否以指定的后缀结束
    boolean equals(Object anObject): 将此字符串与指定的对象比较
    boolean equalsIgnoreCase(String anotherString): 将此 String 与另一个 String 比较,不考虑大小写
    byte[] getBytes(): 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中
    void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):    将字符从此字符串复制到目标字符数组
    int hashCode(): 返回此字符串的哈希码
    int indexOf(int ch): 返回指定字符在此字符串中第一次出现处的索引
    int lastIndexOf(int ch): 返回指定字符在此字符串中最后一次出现处的索引
    boolean matches(String regex): 告知此字符串是否匹配给定的正则表达式
    boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len):
        测试两个字符串区域是否相等
    String[] split(String regex): 根据给定正则表达式的匹配拆分此字符串
    boolean startsWith(String prefix): 测试此字符串是否以指定的前缀开始
    String substring(int beginIndex): 返回一个新的字符串,它是此字符串的一个子字符串
    char[] toCharArray(): 将此字符串转换为一个新的字符数组
    String toLowerCase(): 使用默认语言环境的规则将此 String 中的所有字符都转换为小写
    String toUpperCase(): 使用默认语言环境的规则将此 String 中的所有字符都转换为大写
    String trim(): 返回字符串的副本,忽略前导空白和尾部空白
    

Java StringBuffer和StringBuilder类

对字符串进行修改的时候,需要使用StringBuffer和StringBuilder类
和String类不同的是,StringBuffer和StringBuilder类的对象能够被多次的修改,且不产生新的未使用对象
StringBuffer和StringBuilder类之间的最大不同在于StringBuilder的方法不是线程安全的(不能同步访问)
由于StringBuilder相较于StringBuffer有速度优势,大多数情况下建议使用StringBuilder类.
然而在应用程序要求线程安全的情况下,则必须使用StringBuffer类
//例子
public class Main {

    public static void main(String[] args) {
        StringBuffer sBuffer = new StringBuffer("test");
        sBuffer.append(" String Buffer");
        System.out.println(sBuffer);    //test String Buffer
    }
}

StringBuffer 方法
    public StringBuffer append(String s): 将指定的字符串追加到此字符序列
    public StringBuffer reverse(): 将此字符序列用其反转形式取代
    public delete(int start, int end): 移除此序列的子字符串中的字符
    public insert(int offset, int i): 将 int 参数的字符串表示形式插入此序列中
    replace(int start, int end, String str): 使用给定 String 中的字符替换此序列的子字符串中的字符
    ...

Java数组

用来存储固定大小的同类型元素
double[] myList;    //首选的声明写法
myList = new double[1024];    //创建数组

数组作为函数的参数
    数组可以作为参数传递给方法

数组作为函数的返回值

public class Main {

    public static void printArray(double[] array){
        for(int i = 0;i < array.length;i++){
            System.out.println(array[i] + " ");
        }
    }
    public static double[] reverse(double[] list){
        double[] result = new double[list.length];
        for(int i = 0, j = result.length - 1; i < list.length; i++, j--){
            result[j] = list[i];
        }
        return result;
    }
    public static void main(String[] args) {
        StringBuffer sBuffer = new StringBuffer("test");
        sBuffer.append(" String Buffer");
        System.out.println(sBuffer);    //test String Buffer

        double[] myList = {1.9, 2.9, 3.4, 3.5};
        //打印所有数组元素
        for(int i = 0; i < myList.length; i++){
            System.out.println(myList[i] + " ");
        }
        for(double ele:myList){
            System.out.println(ele);
        }
        printArray(myList);
        //计算所有元素的总和
        double total = 0;
        for(int i = 0;i < myList.length;i++){
            total += myList[i];
        }
        System.out.println("Total is " + total);
        //查找最大元素
        double max = myList[0];
        for(int i = 1;i < myList.length; i++){

            if(myList[i] > max){
                max = myList[i];
            }
        }
        System.out.println("Max is " + max);

        double[] reverse = reverse(myList);
        printArray(reverse);
    }
}

Arrays类
    java.util.Arrays类能方便地操作数组,它提供的所有方法都是静态的.
    1.给数组赋值: 通过fill方法
    2.对数组排序: 通缩sort方法,按升序.
    3.比较数组: 通过equals方法比较数组中元素值是否相等
    4.查找数组元素: 通过binarySearch方法能对排序好的数组进行二分查找法操作
    public static void fill(int[] a, int val)
    public static void sort(Object[] a)
    public static boolean equals(long[] a, long[] a2)
    public static int binarySearch(Object[] a, Object key)

Java日期时间

java.util包提供了Date类来封装当前的日期和时间,Date类提供两个构造函数来实例化Date对象
1.Date(): 使用当前日期和时间来初始化对象
2.Date(long millisec): 该参数从1970年一月一日起的微秒数
Date对象创建后,可调用下面的方法:
    boolean after(Date date): 若当调用此方法的Date对象在指定日期之后返回true,否则返回false
    boolean before(Date date): 若当调用此方法的Date对象在指定日期之前返回true,否则返回false
    Object clone(): 返回此对象的副本
    int compareTo(Date date): 比较当调用此方法的Date对象和指定日期。两者相等时候返回0。调用对象在指定日期之前则返回负数。调用对象在指定日期之后则返回正数
    int compareTo(Object obj): 若obj是Date类型则操作等同于compareTo(Date) 。否则它抛出ClassCastException
    boolean equals(Object date): 当调用此方法的Date对象和指定日期相等时候返回true,否则返回false
    long getTime(): 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数
    int hashCode(): 返回此对象的哈希码值
    void setTime(long time): 用自1970年1月1日00:00:00 GMT以后time毫秒数设置时间和日期
    String toString(): 转换Date对象为String表示形式,并返回该字符串

使用SimpleDateFormat格式化日期
    import java.text.SimpleDateFormat;
    import java.util.Date;

    //时间日期专题
    public class Main {

        public static void main(String[] args) {
            //初始化 Date 对象
            Date date = new Date();
            //使用toString()函数显示日期时间
            System.out.println(date.toString());    //Thu Jun 06 17:13:04 CST 2019

            //使用SimpleDateFormat格式化日期
            SimpleDateFormat ft = new SimpleDateFormat("E yyyy.MM.dd 'at' hh:mm:ss a zzz");
            System.out.println("Current Date: " + ft.format(date)); //Current Date: 周四 2019.06.06 at 05:17:07 下午 CST
        }
    }
    
使用printf格式化日期
    import java.util.Date;
    public class DateDemo {

      public static void main(String args[]) {
         // 初始化 Date 对象
         Date date = new Date();

         // 使用toString()显示日期和时间
         String str = String.format("Current Date/Time : %tc", date );

         System.out.printf(str);
      }
    }
    /---------------------------------------/
    import java.util.Date;
    public class DateDemo {

       public static void main(String args[]) {
           // 初始化 Date 对象
           Date date = new Date();
            
           // 使用toString()显示日期和时间
           System.out.printf("%1$s %2$tB %2$td, %2$tY", 
                             "Due date:", date);
       }
    }
    DateFormat格式化编码
    日期和时间转换字符
    解析字符串为时间
        SimpleDateFormat类有一些附加的方法,特别是parse(),它试图按照给定的SimpleDateFormat对象的格式化存储
        来解析字符串
        
    Java 休眠(sleep)
        可以让程序休眠
            Thread.sleep(5*60*10);
            
    测量时间
        long start = System.currentTimeMillis();
        System.out.println(new Date() + "\n");
        Thread.sleep(5*60*10);  //休眠10秒,不准确
        System.out.println(new Date() + "\n");
        long end = System.currentTimeMillis();
        long diff = end - start;
        System.out.println("Difference is :" + diff/1000);    //秒

Calendar类
    如何设置和获取日期数据的特定部分,比如小时,日,或者分钟?
    使用Calendar类
        Calendar类的功能要比Date类强大很多,而且实现方式上也比Date类要复杂一些
        Calendar类是一个抽象类,在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说时透明的
        ,只需使用getInstance方法创建即可
        Calendar c = Calendar.getInstance();    //默认是当前日期
        
GregorianCalendar类
    Calendar类实现了公历日历,GregorianCalendar是Calendar类的一个具体实现
    import java.util.Calendar;
    import java.util.GregorianCalendar;

    public class Main {
        public static void main(String[] args){
            String months[] = {"Jan", "Feb", "Mar", "Apr",
                                "May", "Jun", "Jul", "Aug",
                                "Sep", "Oct", "Nov", "Dec"};

            int year;
            //初始化Gregorian 日历
            //使用当前时间和日期
            //默认为本地时间和时区
            GregorianCalendar gcalendar = new GregorianCalendar();
            //显示当前时间和日期的信息
            System.out.print("Date: ");
            System.out.print(months[gcalendar.get(Calendar.MONTH)]);
            System.out.print(" "+ gcalendar.get(Calendar.DATE) + " ");
            System.out.println(year = gcalendar.get(Calendar.YEAR));
            System.out.print("Time: ");
            System.out.print(gcalendar.get(Calendar.HOUR) + ":");
            System.out.print(gcalendar.get(Calendar.MINUTE) + ":");
            System.out.println(gcalendar.get(Calendar.SECOND));

            //判断当前年份是否为润年
            if(gcalendar.isLeapYear(year)){
                System.out.println("当前年份是润年");
            }else{
                System.out.println("当前年份不是润年");
            }
        }
    }

JAVA方法

方法是解决一类问题的步骤的有序组合
方法包含于类和对象中
方法在程序中被创建,在其它地方被引用
方法的定义:
    修饰符 返回值类型 方法名(参数类型 参数名)
        ...
        方法体
        ...
        return 返回值
    main方法是被JVM调用
重载的方法必须具有不同的参数列表,不能仅仅依据修饰符或返回类型的不同来重载方法

构造方法:
    所有的类都有构造方法,因为JAVA自动提供了一个默认构造方法,它把所有成员初始化为0.
    一旦你定义了自己的构造方法,默认构造方法就会失效
finalize()方法:
    JAVA允许定义这样的方法,它在对象被垃圾收集器析构(回收)之前调用,用来清除回收对象
    例如:可以使用finalize()方法来确保一个对象打开的文件被关闭了.
    在finalize()方法里,必须指定在对象销毁时候要执行的操作.
    

JAVA流、文件、IO

输入流:表示从一个源读取数据
输出流: 表示向一个目标写数据
读取控制台输入:
    JAVA的控制台输入由System.in完成
    为了获得一个绑定到控制台的字符流,可以把System.in包装在BufferedReader对象中来创建一个字符流
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in))
        BufferedReader对象创建后,我们可以使用read()方法从控制台读取一个字符,或者用readLine()方法来读取一个字符串
        
//读写文件,一个流被定义为一个数据序列,输入流用于从源读取数据,输出流用于向目标写数据
//Object---(OutputStream, InputStream)
//OutputStream---(FilterOutputStream,FileOutputStream,ByteArrayOutputStream)
//FilterOutputStream---(BufferedOutputStream, DataOutputStream, PrintStream)
//InputStream---(ByteArrayInputStream,FileInputStream,FilterInputStream,StringBufferInputStream,SequenceInputStream)
//FilterInputStream---(BufferedInputStream, DataInputStream, PushbackInputStream)

//FileInputStream
//该流用于从文件读取数据,用关键字new来创建对象

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class Main {

    public static void main(String[] args) {
        try{
            //创建输入流对象来读取文件
            FileInputStream f = new FileInputStream("C:\\Users\\jasmintung\\meiju\\main.cpp");
            File ff = new File("C:\\Users\\jasmintung\\meiju\\main.cpp");
            //也可使用一个文件对象来创建一个输入输出流对象来读取文件
            InputStream fs = new FileInputStream(ff);
        }catch(FileNotFoundException ex) {
            System.out.println(ex);
        }
    }
}
创建了InputStream对象,就可以使用下面方法来读取流或进行其它的操作
    1:public void close() throws IOException{}
        关闭此文件输入流并释放与此流有关的所有系统资源。抛出IOException异常。
    2:protected void finalize()throws IOException {}
        这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出IOException异常
    3:public int read(int r)throws IOException{}
        这个方法从InputStream对象读取指定字节的数据。返回为整数值。返回下一字节数据,如果已经到结尾则返回-1
    4:public int read(byte[] r) throws IOException{}
        这个方法从输入流读取r.length长度的字节。返回读取的字节数。如果是文件结尾则返回-1
    5:public int available() throws IOException{}
        返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取的字节数。返回一个整数值
        
//FileOutputStream
该类用来创建一个文件并向文件中写数据

//FileReader,FileWriter类

//JAVA中的目录
创建目录: mkdir(),mkdirs()

//读取目录:
    一个目录其实就是一个File对象,它包含其它文件或文件夹
    如果创建一个File对象并且它是一个目录,那么调用isDirectory()方法会返回true
    可通过调用该对象上的list()方法,来提取它包含的文件和文件夹的列表
    import java.io.File;

    public class DirList {
       public static void main(String args[]) {
          String dirname = "/tmp";
          File f1 = new File(dirname);
          if (f1.isDirectory()) {
             System.out.println( "Directory of " + dirname);
             String s[] = f1.list();
             for (int i=0; i < s.length; i++) {
                File f = new File(dirname + "/" + s[i]);
                if (f.isDirectory()) {
                   System.out.println(s[i] + " is a directory");
                } else {
                   System.out.println(s[i] + " is a file");
                }
             }
          } else {
             System.out.println(dirname + " is not a directory");
        }
      }
    }
    

JAVA异常处理

异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候可以避免
异常发生的原因有很多,通常包含一下几大类:
    1.用户输入了非法数据
    2.要打开的文件不存在
    3.网络通信时连接中断,或者JVM内存溢出
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
    检查性异常: 最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
    运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
    错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的
Exception类的层次:
    所有的异常类都是从java.lang.Exception类继承的子类
    Exception类是Throwable类的子类.
    除了Exception类外,Throwable还有一个子类Error.
    
    在Java内置类中,有大部分常用检查性和非检查性异常
    Java 语言定义了一些异常类在java.lang标准包中
    由于java.lang包是默认加载到所有的Java程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用
    
捕获异常:
    使用try和catch关键字可以捕获异常.

多重捕获块
    一个try代码块后面跟随多个catch代码块的情况叫多重捕获
throws/throw关键字
    如果一个方法没有捕获一个检查性异常,那么该方法必须使用throws关键字类声明
    throws关键字放在方法签名的尾部
    
    import java.io.*;
    public class className
    {
       public void withdraw(double amount) throws RemoteException,
                                  InsufficientFundsException
       {
           // Method implementation
       }
       //Remainder of class definition
    }
    
finally关键字
    finally关键字用来创建在try代码块后面执行的代码块
    无论是否发生异常,finally代码块中的代码总会被执行
    在finally代码块中,可以运行清理类型等收尾善后性质的语句
    finally代码块出现在catch代码块最后
    
    try{
        // 程序代码
     }catch(异常类型1 异常的变量名1){
        // 程序代码
     }catch(异常类型2 异常的变量名2){
        // 程序代码
     }finally{
        // 程序代码
     }
     
声明自定义异常
    在java中可以自定义异常
    注意:
        1.所有异常都必须是Throwable的子类
        2.如果希望写一个检查性异常类,则需要继承Exception类
        3.如果想写一个运行时异常,那么需要继承RuntimeException类
    例如:
        class MyException extends Exception{
            //
        }
        
        一个异常类和其它任何类一样,包含有变量和方法
    如何使用自定义的异常类:
        // 文件名InsufficientFundsException.java
        import java.io.*;

        public class InsufficientFundsException extends Exception
        {
           private double amount;
           public InsufficientFundsException(double amount)
           {
              this.amount = amount;
           } 
           public double getAmount()
           {
              return amount;
           }
        }
        
        // 文件名称 CheckingAccount.java
        import java.io.*;

        public class CheckingAccount
        {
           private double balance;
           private int number;
           public CheckingAccount(int number)
           {
              this.number = number;
           }
           public void deposit(double amount)
           {
              balance += amount;
           }
           public void withdraw(double amount) throws
                                      InsufficientFundsException
           {
              if(amount <= balance)
              {
                 balance -= amount;
              }
              else
              {
                 double needs = amount - balance;
                 throw new InsufficientFundsException(needs);
              }
           }
           public double getBalance()
           {
              return balance;
           }
           public int getNumber()
           {
              return number;
           }
        }
        
        //文件名称 BankDemo.java
        public class BankDemo
        {
           public static void main(String [] args)
           {
              CheckingAccount c = new CheckingAccount(101);
              System.out.println("Depositing $500...");
              c.deposit(500.00);
              try
              {
                 System.out.println("\nWithdrawing $100...");
                 c.withdraw(100.00);
                 System.out.println("\nWithdrawing $600...");
                 c.withdraw(600.00);
              }catch(InsufficientFundsException e)
              {
                 System.out.println("Sorry, but you are short $"
                                          + e.getAmount());
                 e.printStackTrace();
              }
            }
        }
通用异常
    在java中定义了两种类型的异常和错误
    1.JVM异常: 由JVM抛出的异常或错误.如:NullPointerException类,
        ArrayIndexOutOfBoundsException类,ClassCastException类
    2.程序级异常: 由程序或者API程序抛出的异常
        例如IllegalArgumentException类,IllegalStateException类
        

JAVA ByteArrayInputStream类

字节数组输入流在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区.
用法:
    ByteArrayInputStream bArray = new ByteArrayInputStream(byte[] a);
    ByteArrayInputStream bArray = new ByteArrayInputStream(byte[] a, int off, int len);
    
成功创建字节数组流对象后,可用以下方法对流进行读操作或其它操作:
    1.public int read(): 从此输入流中读取下一个数据字节
    2.public int read(byte[] r, int off, int len): 将最多 len 个数据字节从此输入流读入字节数组
    3.public int available(): 返回可不发生阻塞地从此输入流读取的字节数
    4.public void mark(int read): 设置流中的当前标记位置
    5.public long skip(long n): 从此输入流中跳过 n 个输入字节
    
例子:
    import java.io.*;

    public class ByteStreamTest {
       public static void main(String args[])throws IOException {
          ByteArrayOutputStream bOutput = new ByteArrayOutputStream(12);

          while( bOutput.size()!= 10 ) {
             // 获取用户输入值
             bOutput.write(System.in.read());
          }

          byte b [] = bOutput.toByteArray();
          System.out.println("Print the content");
          for(int x= 0 ; x < b.length; x++) {
             // 打印字符
             System.out.print((char)b[x]  + "   ");
          }
          System.out.println("   ");

          int c;

          ByteArrayInputStream bInput = new ByteArrayInputStream(b);

          System.out.println("Converting characters to Upper case " );
          for(int y = 0 ; y < 1; y++ ) {
             while(( c= bInput.read())!= -1) {
                System.out.println(Character.toUpperCase((char)c));
             }
             bInput.reset();
          }
       }
    }
    

JAVA DataInputStream类

数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本JAVA数据类型
用法:
    DataInputStream dis = DataInputStream(InputStream in);
方法:
    1.public final int read(byte[] r, int off, int len)throws IOException
        从所包含的输入流中将 len 个字节读入一个字节数组中。如果len为-1,则返回已读字节数
    2.Public final int read(byte [] b)throws IOException
        从所包含的输入流中读取一定数量的字节,并将它们存储到缓冲区数组 b 中
    3.public final Boolean readBooolean()throws IOException,
        public final byte readByte()throws IOException,
        public final short readShort()throws IOException
        public final Int readInt()throws IOException
        从输入流中读取字节,返回输入流中两个字节作为对应的基本数据类型返回值
    4.public String readLine() throws IOException
        从输入流中读取下一文本行
例子:
    DataInputStream和DataOutputStream的使用,该例从文本文件test.txt中读取5行,并转换成大写字母,最后保存在另一个文件test1.txt中
    
    import java.io.*;

    public class Test{
       public static void main(String args[])throws IOException{

          DataInputStream d = new DataInputStream(new
                                   FileInputStream("test.txt"));

          DataOutputStream out = new DataOutputStream(new
                                   FileOutputStream("test1.txt"));

          String count;
          while((count = d.readLine()) != null){
              String u = count.toUpperCase();
              System.out.println(u);
              out.writeBytes(u + "  ,");
          }
          d.close();
          out.close();
       }
    }
    

JAVA ByteArrayOutputStream 类

字节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中
用法:
    OutputStream bOut = new ByteArrayOutputStream();
    OutputStream bOut = new ByteArrayOutputStream(int a)
方法:
    1.public void reset()
        将此字节数组输出流的 count 字段重置为零,从而丢弃输出流中目前已累积的所有数据输出
    2.public byte[] toByteArray()
        创建一个新分配的字节数组。数组的大小和当前输出流的大小,内容是当前输出流的拷贝
    3.public String toString()
        将缓冲区的内容转换为字符串,根据平台的默认字符编码将字节转换成字符
    4.public void write(int w)
        将指定的字节写入此字节数组输出流
    5.public void write(byte []b, int of, int len)
        将指定字节数组中从偏移量 off 开始的 len 个字节写入此字节数组输出流
    6.public void writeTo(OutputStream outSt)
        将此字节数组输出流的全部内容写入到指定的输出流参数中
    
    

JAVA DataoutputStream 类

数据输出流允许应用程序以与机器无关方式将Java基本数据类型写到底层输出流
用法:
    DataOutputStream out = DataOutputStream(OutputStream  out);
方法:
    1.public final void write(byte[] w, int off, int len)throws IOException
        将指定字节数组中从偏移量 off 开始的 len 个字节写入此字节数组输出流
    2.Public final int write(byte [] b)throws IOException
        将指定的字节写入此字节数组输出流
    3.public final void writeBooolean()throws IOException,
        public final void writeByte()throws IOException,
        public final void writeShort()throws IOException,
        public final void writeInt()throws IOException
        这些方法将指定的基本数据类型以字节的方式写入到输出流
    4.Public void flush()throws IOException
        刷新此输出流并强制写出所有缓冲的输出字节
    5.public final void writeBytes(String s) throws IOException
        将字符串以字节序列写入到底层的输出流,字符串中每个字符都按顺序写入,并丢弃其高八位
    

JAVA File类

JAVA文件类以抽象的方式代表文件名和目录路径名.该类主要用于文件和目录的创建,文件的查找和文件的删除等
File对象代表磁盘中实际存在的文件和目录
用法:
    File(File parent, String child);
    File(String pathname);
    File(String parent, String child);
    File(URI uri);
    
方法:
    1    public String getName()
    返回由此抽象路径名表示的文件或目录的名称。
    2    public String getParent()、
     返回此抽象路径名的父路径名的路径名字符串,如果此路径名没有指定父目录,则返回 null。
    3    public File getParentFile()
    返回此抽象路径名的父路径名的抽象路径名,如果此路径名没有指定父目录,则返回 null。
    4    public String getPath()
    将此抽象路径名转换为一个路径名字符串。
    5    public boolean isAbsolute()
    测试此抽象路径名是否为绝对路径名。
    6    public String getAbsolutePath()
    返回抽象路径名的绝对路径名字符串。
    7    public boolean canRead()
    测试应用程序是否可以读取此抽象路径名表示的文件。
    8    public boolean canWrite()
    测试应用程序是否可以修改此抽象路径名表示的文件。
    9    public boolean exists()
    测试此抽象路径名表示的文件或目录是否存在。
    10    public boolean isDirectory()
    测试此抽象路径名表示的文件是否是一个目录。
    11    public boolean isFile()
    测试此抽象路径名表示的文件是否是一个标准文件。
    12    public long lastModified()
    返回此抽象路径名表示的文件最后一次被修改的时间。
    13    public long length()
    返回由此抽象路径名表示的文件的长度。
    14    public boolean createNewFile() throws IOException
    当且仅当不存在具有此抽象路径名指定的名称的文件时,原子地创建由此抽象路径名指定的一个新的空文件。
    15    public boolean delete()
     删除此抽象路径名表示的文件或目录。
    16    public void deleteOnExit()
    在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。
    17    public String[] list()
    返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组。
    18    public String[] list(FilenameFilter filter)
    返回由包含在目录中的文件和目录的名称所组成的字符串数组,这一目录是通过满足指定过滤器的抽象路径名来表示的。
    19    public File[] listFiles()
      返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件。
    20    public File[] listFiles(FileFilter filter)
    返回表示此抽象路径名所表示目录中的文件和目录的抽象路径名数组,这些路径名满足特定过滤器。
    21    public boolean mkdir()
    创建此抽象路径名指定的目录。
    22    public boolean mkdirs()
    创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。
    23    public boolean renameTo(File dest)
     重新命名此抽象路径名表示的文件。
    24    public boolean setLastModified(long time)
    设置由此抽象路径名所指定的文件或目录的最后一次修改时间。
    25    public boolean setReadOnly()
    标记此抽象路径名指定的文件或目录,以便只可对其进行读操作。
    26    public static File createTempFile(String prefix, String suffix, File directory) throws IOException
    在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。
    27    public static File createTempFile(String prefix, String suffix) throws IOException
    在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。
    28    public int compareTo(File pathname)
    按字母顺序比较两个抽象路径名。
    29    public int compareTo(Object o)
    按字母顺序比较抽象路径名与给定对象。
    30    public boolean equals(Object obj)
    测试此抽象路径名与给定对象是否相等。
    31    public String toString()
     返回此抽象路径名的路径名字符串
     
例子:
    import java.io.File;
    public class DirList {
       public static void main(String args[]) {
          String dirname = "/java";
          File f1 = new File(dirname);
          if (f1.isDirectory()) {
             System.out.println( "Directory of " + dirname);
             String s[] = f1.list();
             for (int i=0; i < s.length; i++) {
                File f = new File(dirname + "/" + s[i]);
                if (f.isDirectory()) {
                   System.out.println(s[i] + " is a directory");
                } else {
                   System.out.println(s[i] + " is a file");
                }
             }
          } else {
             System.out.println(dirname + " is not a directory");
        }
      }
    }

JAVA FileReader类

FileReader类从InputStreamReader类继承而来,该类按字符读取流中数据.
用法:
    FileReader(File file);
    FileReader(FileDescriptor fd);
    FileReader(String fileName;
方法:
    1    public int read() throws IOException
    读取单个字符,返回一个int型变量代表读取到的字符
    2    public int read(char [] c, int offset, int len)
    读取字符到c数组,返回读取到字符的个数
例子:
    import java.io.*;
    public class FileRead{
       public static void main(String args[])throws IOException{
          File file = new File("Hello1.txt");
          // 创建文件
          file.createNewFile();
          // creates a FileWriter Object
          FileWriter writer = new FileWriter(file); 
          // 向文件写入内容
          writer.write("This\n is\n an\n example\n"); 
          writer.flush();
          writer.close();
          // 创建 FileReader 对象
          FileReader fr = new FileReader(file); 
          char [] a = new char[50];
          fr.read(a); // 读取数组中的内容
          for(char c : a)
              System.out.print(c); // 一个一个打印字符
          fr.close();
       }
    }
    

JAVA FileWriter类

FileWriter类从OutputStreamReader类继承而来。该类按字符向流中写入数据
用法:
    FileWriter(File file);
    FileWriter(File file, boolean append);
    FileWriter(FileDescriptor fd);
    FileWriter(String fileName, boolean append);
方法:
    1    public void write(int c) throws IOException
    写入单个字符c。
    2    public void write(char [] c, int offset, int len)
    写入字符数组中开始为offset长度为len的某一部分。
    3    public void write(String s, int offset, int len)
    写入字符串中开始为offset长度为len的某一部分。

Java 继承

JAVA中,类的继承是单一继承,一个子类只能拥有一个父类
继承中最常使用的两个关键字: extends 和 implements
这两个关键字的使用决定了一个对象和另一个对象是否是IS-A(是一个)关系
通过使用这两个关键字,我们能实现一个对象获取另一个对象的属性
所有JAVA的类均是由java.lang.Object类继承而来的,所以Object是所有类的祖先类,
除了Object外,所有类必须有一个父类
// A.java
public class A {
    private int i;
    protected int j;
 
    public void func() {
 
    }
}
 
// B.java
public class B extends A {

}
B由A继承而来的, B是A的子类. 而A是Object的子类, 这里可以不显示地声明
作为子类,B的实例拥有A所有的成员变量,但对于 private 的成员变量B却没有访问权限,这保障了A的封装性

什么是IS-A关系?
    一个对象是另一个对象的一个分类
    public class Animal{
    }

    public class Mammal extends Animal{
    }

    public class Reptile extends Animal{
    }

    public class Dog extends Mammal{
    }
    分析以上示例中的IS-A关系,如下:
        Mammal IS-A Animal
        Reptile IS-A Animal
        Dog IS-A Mammal
        因此 : Dog IS-A Animal
通过使用关键字 extends,子类可以继承父类的除private属性外所有的属性.
我们通过使用 instanceof 操作符,能够确定Mammal IS-A Animal(返回true or false)
    public class Dog extends Mammal{

       public static void main(String args[]){

          Animal a = new Animal();
          Mammal m = new Mammal();
          Dog d = new Dog();

          System.out.println(m instanceof Animal);    //true
          System.out.println(d instanceof Mammal);    //true
          System.out.println(d instanceof Animal);    //true
       }
    }

implements 关键字
    使用在类继承接口的情况下.这种情况下不能使用extends

HAS-A 关系
    代表类和它的成员之间的从属关系.有助于代码的重用和减少代码的错误
    例子:
        public class Vehicle{}
        public class Speed{}
        public class Van extends Vehicle{
            private Speed sp;
        }
        Van类和Speed类是HAS-A关系(Van有一个Speed),这样就不用将Speed类的全部代码粘贴到Van类中了
        并且Speed类也可以重复利用于多个应用程序
    
JAVA只支持单继承(继承基本类和抽象类),但是我们可以用接口来实现(多继承接口来实现)
    public class Apple extends Fruit implements Fruit1, Fruit2{
    
    }
    
一般我们继承基本类和抽象类用 extends 关键字,实现接口类的继承用 implements 关键字

JAVA重写Override与重载Overload

重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参不能改变
重写的好处在于子类可以根据需求,定义特定于自己的行为
也就是说子类能够根据需要实现父类的方法
例子:
    class Animal{
        public void move(){
            System.out.println("动物可以移动");
        }
    }
    
    class Dog extends Animal{
        public void move(){
            System.out.println("dog can run and walk");
        }
    }
    
    public class TestDog{
        public static void main(String args[]){
            Animal a = new Animal();
            Animal b = new Dog();    //Dog对象
            a.move();    //执行 Animal 类的方法
            b.moev();    //执行 Dog 类的方法
        }
    }
    
    在上面的例子中可以看到,尽管b属于Animal类型,但是它运行的是Dog类的move方法。
    这是由于在编译阶段,只是检查参数的引用类型。
    然而在运行时,Java虚拟机(JVM)指定对象的类型并且运行该对象的方法。
    因此在上面的例子中,之所以能编译成功,是因为Animal类中存在move方法,然而运行时,运行的是特定对象的方法
    
思考例子:
    class Animal{

       public void move(){
          System.out.println("动物可以移动");
       }
    }

    class Dog extends Animal{

       public void move(){
          System.out.println("狗可以跑和走");
       }
       public void bark(){
          System.out.println("狗可以吠叫");
       }
    }

    public class TestDog{

       public static void main(String args[]){
          Animal a = new Animal(); // Animal 对象
          Animal b = new Dog(); // Dog 对象

          a.move();// 执行 Animal 类的方法
          b.move();//执行 Dog 类的方法
          b.bark();
       }
    }
    运行结果:
        TestDog.java:30: cannot find symbol
        symbol  : method bark()
        location: class Animal
                        b.bark();
                        
    该程序将抛出一个编译错误,因为b的引用类型Animal没有bark方法
    
方写重写的规则
    参数列表必须完全与被重写方法的相同;
    返回类型必须完全与被重写方法的返回类型相同;
    访问权限不能比父类中被重写的方法的访问权限更高。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
    父类的成员方法只能被它的子类重写。
    声明为 final 的方法不能被重写。
    声明为 static 的方法不能被重写,但是能够被再次声明。
    如果一个方法不能被继承,那么该方法不能被重写。
    子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
    子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
    重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
    构造方法不能被重写。
    如果不能继承一个方法,则不能重写这个方法

Super关键字的使用
    当需要在子类中调用父类的被重写方法时,要使用super关键字
    class Animal{
        public void move(){
            System.out.println("anmial can move");
        }
    }
    
    class Dog extends Animall{
        public void move(){
            super.move();    //调用 super类的方法
            System.out.println("dog can run and walk");
        }
    }
    
    public class TestDog{
        public static void main(String args[]){
            Animal b = new Dog();
            b.move();
        }
    }
    
重载(Overload)
    重载(overloading) 是在一个类里面, 方法名字相同, 而参数不同.返回类型呢?可以相同也可以不同
    
    重载规则:
        被重载的方法必须改变参数列表;
        被重载的方法可以改变返回类型;
        被重载的方法可以改变访问修饰符;
        被重载的方法可以声明新的或更广的检查异常;
        方法能够在同一个类中或者在一个子类中被重载
        
    例子:
        public class Overloading {

        public int test(){
            System.out.println("test1");
            return 1;
        }
     
        public void test(int a){
            System.out.println("test2");
        }    
     
        //以下两个参数类型顺序不同
        public String test(int a,String s){
            System.out.println("test3");
            return "returntest3";
        }    
     
        public String test(String s,int a){
            System.out.println("test4");
            return "returntest4";
        }    
     
        public static void main(String[] args){
            Overloading o = new Overloading();
            System.out.println(o.test());
            o.test(1);
            System.out.println(o.test(1,"test3"));
            System.out.println(o.test("test4",1));
        }

重写与重载之间的区别
    区别点       重载方法            重写方法
    参数列表    必须修改         一定不能修改
    返回类型    可以修改         一定不能修改
    异常        可以修改         可以减少或删除,一定不能抛出新的或者更广的异常
    访问        可以修改         一定不能做更严格的限制(可以降低限制)    
    

JAVA多态

多态,是同一个行为具有多个不同表现形式或形态的能力
比如我们说"宠物"这个对象,它就有很多不同的表达或实现,比如有小猫、小狗、蜥蜴等等。
那么我到宠物店说"请给我一只宠物",服务员给我小猫、小狗或者蜥蜴都可以,我们就说"宠物"这个对象就具备多态性

例子:
    public interface Vegetarian{}
    public class Animal{}
    public class Deer extends Animal implements Vegetarian{}
    此时,Deer类具有多重继承,具有多态性
        一个 Deer IS-A(是一个) Animal
        一个 Deer IS-A(是一个) Vegetarian
        一个 Deer IS-A(是一个) Deer
        一个 Deer IS-A(是一个)Object
在JAVA中,所有的对象都具有多态性.
访问一个对象的唯一方法是: 通过引用型变量
引用型变量只能有一种类型,一旦被声明,引用型变量的类型就不能被改变了
引用型变量不仅能够被重置为其它对象,前提是这些对象没有被声明为final.
还可以引用和它类型相同的或者相兼容的对象.它可以声明为类类型或者接口类型

当我们将引用变量应用于Deer对象的引用时,下面的声明时合法的:
    Deer d = new Deer();
    Animal a = d;
    Vegetarian v = d;
    Object o = d;
    所有的引用型变量d,a,v,o都指向堆中相同的Deer对象

虚方法(可以理解为C++中的虚函数)
    当子类对象调用重载的方法时,调用的是子类的方法,而不是父类中被重载的方法
    想要调用父类中被重载的方法,必须使用关键字super

    例子(仔细看):
        /* 文件名 : Employee.java */
        public class Employee
        {
           private String name;
           private String address;
           private int number;
           public Employee(String name, String address, int number)
           {
              System.out.println("Constructing an Employee");
              this.name = name;
              this.address = address;
              this.number = number;
           }
           public void mailCheck()
           {
              System.out.println("Mailing a check to " + this.name
               + " " + this.address);
           }
           public String toString()
           {
              return name + " " + address + " " + number;
           }
           public String getName()
           {
              return name;
           }
           public String getAddress()
           {
              return address;
           }
           public void setAddress(String newAddress)
           {
              address = newAddress;
           }
           public int getNumber()
           {
             return number;
           }
        }
        
        /* 文件名 : Salary.java */
        public class Salary extends Employee
        {
           private double salary; //Annual salary
           public Salary(String name, String address, int number, double
              salary)
           {
               super(name, address, number);
               setSalary(salary);
           }
           public void mailCheck()
           {
               System.out.println("Within mailCheck of Salary class ");
               System.out.println("Mailing check to " + getName()
               + " with salary " + salary);
           }
           public double getSalary()
           {
               return salary;
           }
           public void setSalary(double newSalary)
           {
               if(newSalary >= 0.0)
               {
                  salary = newSalary;
               }
           }
           public double computePay()
           {
              System.out.println("Computing salary pay for " + getName());
              return salary/52;
           }
        }
        
        /* 文件名 : VirtualDemo.java */
        public class VirtualDemo
        {
           public static void main(String [] args)
           {
              Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
              Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
              System.out.println("Call mailCheck using Salary reference --");
              s.mailCheck();
              System.out.println("\n Call mailCheck using Employee reference--");
              e.mailCheck();
            }
        }
        
        运行结果:
            Constructing an Employee
            Constructing an Employee
            Call mailCheck using Salary reference --
            Within mailCheck of Salary class
            Mailing check to Mohd Mohtashim with salary 3600.0

            Call mailCheck using Employee reference--
            Within mailCheck of Salary class
            Mailing check to John Adams with salary 2400.0

        例子中,我们实例化了两个Salary对象。一个使用Salary引用s,另一个使用Employee引用。
            编译时,编译器检查到mailCheck()方法在Salary类中的声明。
            在调用s.mailCheck()时,Java虚拟机(JVM)调用Salary类的mailCheck()方法。
            因为e是Employee的引用,所以调用e的mailCheck()方法则有完全不同的结果。
            当编译器检查e.mailCheck()方法时,编译器检查到Employee类中的mailCheck()方法。
            在编译的时候,编译器使用Employee类中的mailCheck()方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是Salary类中的mailCheck()方法。
            该行为被称为虚拟方法调用,该方法被称为虚拟方法。
            
            Java中所有的方法都能以这种方式表现,借此,重写的方法能在运行时调用,不管编译的时候源代码中引用变量是什么数据类型。
            

JAVA抽象类

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,
如果一个类中没有包含足够的信息来描述一个具体的对象,这样的类就是抽象类

抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量,成员方法和构造方法的访问方式和普通类一样
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用.也就是因为这个原因,通常在设计阶段决定要不要设计抽象类

定义一个抽象类:
    /* 文件名 : Employee.java */
    public abstract class Employee
    {
       private String name;
       private String address;
       private int number;
       public Employee(String name, String address, int number)
       {
          System.out.println("Constructing an Employee");
          this.name = name;
          this.address = address;
          this.number = number;
       }
       public double computePay()
       {
         System.out.println("Inside Employee computePay");
         return 0.0;
       }
       public void mailCheck()
       {
          System.out.println("Mailing a check to " + this.name
           + " " + this.address);
       }
       public String toString()
       {
          return name + " " + address + " " + number;
       }
       public String getName()
       {
          return name;
       }
       public String getAddress()
       {
          return address;
       }
       public void setAddress(String newAddress)
       {
          address = newAddress;
       }
       public int getNumber()
       {
         return number;
       }
    }
    该Employee类没有什么不同,尽管该类是抽象类,但是它仍然有3个成员变量,7个成员方法和1个构造方法.
    现在如果你尝试如下的例子:
        /* 文件名 : AbstractDemo.java */
        public class AbstractDemo
        {
           public static void main(String [] args)
           {
              /* 以下是不允许的,会引发错误 */
              Employee e = new Employee("George W.", "Houston, TX", 43);

              System.out.println("\n Call mailCheck using Employee reference--");
              e.mailCheck();
            }
        }
        当你尝试编译AbstractDemo类时,会产生如下错误:
            Employee.java:46: Employee is abstract; cannot be instantiated
            Employee e = new Employee("George W.", "Houston, TX", 43);
               ^
            1 error
            
继承抽象类
    /* 文件名 : Salary.java */
    public class Salary extends Employee
    {
       private double salary; //Annual salary
       public Salary(String name, String address, int number, double
          salary)
       {
           super(name, address, number);
           setSalary(salary);
       }
       public void mailCheck()
       {
           System.out.println("Within mailCheck of Salary class ");
           System.out.println("Mailing check to " + getName()
           + " with salary " + salary);
       }
       public double getSalary()
       {
           return salary;
       }
       public void setSalary(double newSalary)
       {
           if(newSalary >= 0.0)
           {
              salary = newSalary;
           }
       }
       public double computePay()
       {
          System.out.println("Computing salary pay for " + getName());
          return salary/52;
       }
    }
    尽管我们不能实例化一个Employee类的对象,但是如果我们实例化一个Salary类对象,该对象将从Employee类继承3个成员变量和7个成员方法
    
抽象方法
    如果想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,
    那么可以在父类中声明该方法为抽象方法
    
    Abstract 关键词同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体
    抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号
    
    public abstract class Employee
    {
       private String name;
       private String address;
       private int number;
       
       public abstract double computePay();
       
       //其余代码
    }
    
    声明抽象方法会造成以下两个结果
        1.如果一个类包含抽象方法,那么该类必须是抽象类
        2.任何子类必须重写该类的抽象方法,或者声明自身为抽象类
    继承抽象方法的子类必须重载该方法.否则,该子类也必须声明为抽象类.
    最终,必须有子类实现该抽象类方法
    如果Salary类继承了Employee类,那么它必须实现computePay()方法
        /* 文件名 : Salary.java */
        public class Salary extends Employee
        {
           private double salary; // Annual salary
          
           public double computePay()
           {
              System.out.println("Computing salary pay for " + getName());
              return salary/52;
           }

           //其余代码
        }

JAVA封装(Encapsulation)

防止该类的代码和数据被外部类定义的代码随机访问
要访问该类的代码和数据,必须通过严格的接口控制
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段
适当的封装可以让程式码更容易理解与维护.
例子:
    /* 文件名: EncapTest.java */
    public class EncapTest{

       private String name;
       private String idNum;
       private int age;

       public int getAge(){
          return age;
       }

       public String getName(){
          return name;
       }

       public String getIdNum(){
          return idNum;
       }

       public void setAge( int newAge){
          age = newAge;
       }

       public void setName(String newName){
          name = newName;
       }

       public void setIdNum( String newId){
          idNum = newId;
       }
    }
    以上实例中public方法是外部类访问该类成员变量的入口
    

JAVA接口

在java编程中是一个抽象类型,是抽象方法的集合.接口通常以interface来声明.
一个类通过继承接口的方式,从而来继承接口的抽象方法

接口并不是类, 类描述对象的属性和方法,接口则包含要类实现的方法.
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法
接口与类相似点:
    一个接口可以有多个方法。
    接口文件保存在.java结尾的文件中,文件名使用接口名。
    接口的字节码文件保存在.class结尾的文件中。
    接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
    接口不能用于实例化对象。
    接口没有构造方法。
    接口中所有的方法必须是抽象方法。
    接口不能包含成员变量,除了static和final变量。
    接口不是被类继承了,而是要被类实现。
    接口支持多重继承。

接口的声明:
    [可见度] interface 接口名称 [extends 其他的类名] {
        // 声明变量
        // 抽象方法
    }
    /* 文件名 : NameOfInterface.java */
    import java.lang.*;
    //引入包

    public interface NameOfInterface
    {
       //任何类型 final, static 字段
       //抽象方法
    }
接口有以下特性:
    接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
    接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键子。
    接口中的方法都是公有的

接口的实现
    当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类
    ... implements 接口名称[, 其他接口, 其他接口..., ...] ...
    
    例子:
        /* 文件名 : MammalInt.java */
        public class MammalInt implements Animal{

           public void eat(){
              System.out.println("Mammal eats");
           }

           public void travel(){
              System.out.println("Mammal travels");
           } 

           public int noOfLegs(){
              return 0;
           }

           public static void main(String args[]){
              MammalInt m = new MammalInt();
              m.eat();
              m.travel();
           }
        }

接口的继承
    一个接口能继承另一个接口,和类之间的继承方式比较相似.接口的继承使用extends关键字,子接口继承父接口的方法
    // 文件名: Sports.java
    public interface Sports
    {
       public void setHomeTeam(String name);
       public void setVisitingTeam(String name);
    }

    // 文件名: Football.java
    public interface Football extends Sports
    {
       public void homeTeamScored(int points);
       public void visitingTeamScored(int points);
       public void endOfQuarter(int quarter);
    }

    // 文件名: Hockey.java
    public interface Hockey extends Sports
    {
       public void homeGoalScored();
       public void visitingGoalScored();
       public void endOfPeriod(int period);
       public void overtimePeriod(int ot);
    }
    Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。
    相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口
    
接口的多重继承
    在接口的多重继承中extends关键字只需要使用一次,在其后跟着继承接口
    public interface Hockey extends Sports, Event

标记接口
    最常用的继承接口是没有包含任何方法的接口
    标识接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情
    标识接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权
    例如:java.awt.event包中的MouseListener接口继承的java.util.EventListener接口定义如下
        package java.util;
        public interface EventListener
        {}
    没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:
    1.建立一个公共的父接口:
        正如EventListener接口,这是由几十个其他接口扩展的Java API,
        你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,
        Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
    2.向一个类添加数据类型:
        这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),
        但是该类通过多态性变成一个接口类型
        

JAVA包

为了更好的组织类,JAVA提供了包机制,用于区别类名的命名空间
包的作用:
    1.把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用
    2.如同文件夹一样,包也采用树形目录的存储方式.同一个包中的类名字是不同的,
        不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,
        应该加上包名加以区别.包可避免名字冲突
    3.包限定了访问权限,拥有包访问权限的类才能访问某个包中类
包语句的语法格式:
    package pkg1[. pkg2...]

创建包
    包声明应该放在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它
    如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中.
    
    /* 文件名: Animal.java */
    package animals;

    interface Animal {
       public void eat();
       public void travel();
    }
    
    package animals;

    /* 文件名 : MammalInt.java */
    public class MammalInt implements Animal{

       public void eat(){
          System.out.println("Mammal eats");
       }

       public void travel(){
          System.out.println("Mammal travels");
       } 

       public int noOfLegs(){
          return 0;
       }

       public static void main(String args[]){
          MammalInt m = new MammalInt();
          m.eat();
          m.travel();
       }
    }
    
import关键字
    为了能够使用某一个包的成员,我们需要在java程序中明确导入该包.使用"import"语句可完成此功能
    在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为
        import package1[.package2…].(classname|*);
        如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略
        
    几种导入包的写法:
        1.payroll.Employee
        2.import payroll.*;
        3.import payroll.Employee;
        
    类文件中可以包含任意数量的import声明.import声明必须在包声明之后,类声明之前
    
package的目录结构
    管理java中文件的一种简单方式:
        将类、接口等类型的源码放在一个文本中,这个文件的名字就是这个类型的名字,并以.java作为扩展名
        接下来,把源文件放在一个目录中,这个目录要对应类所在包的名字
        
        通常,一个公司使用它互联网域名的颠倒形式来作为它的包名.例如:互联网域名是apple.com,所有的包名都以com.apple开头.
        包名中的每一个部分对应一个子目录
        
        例如:这个公司有一个com.apple.computers的包,这个包包含一个叫做Dell.java的源文件,那么相应的,应该有如下面的一连串子目录:
            ....\com\apple\computers\Dell.java
            编译的时候,编译器为包中定义的每个类、接口等类型各创建一个不同的输出文件,输出文件的名字就是这个类型的名字,并加上.class作为扩展后缀。 例如:
                // 文件名: Dell.java
                package com.apple.computers;
                public class Dell{
                      
                }
                class Ups{
                      
                }
                用-d选项来编译这个文件:
                $javac -d . Dell.java    
                这样会像下面这样放置编译了的文件:
                .\com\apple\computers\Dell.class.\com\apple\computers\Ups.class
                编译之后的.class文件应该和.java源文件一样,它们放置的目录应该跟包的名字对应起来。但是,并不要求.class文件的路径跟相应的.java的路径一样。
                你可以分开来安排源码和类的目录:
                <path-one>\sources\com\apple\computers\Dell.java
                <path-two>\classes\com\apple\computers\Dell.class
                这样,你可以将你的类目录分享给其他的编程人员,而不用透露自己的源码。用这种方法管理源码和类文件可以让编译器和java虚拟机(JVM)可以找到你程序中使用的所有类型。

                类目录的绝对路径叫做class path。设置在系统变量CLASSPATH中。编译器和java虚拟机通过将package名字加到class path后来构造.class文件的路径。
                <path- two>\classes是class path,package名字是com.apple.computers,而编译器和JVM会在 <path-two>\classes\com\apple\compters中找.class文件。
                一个class path可能会包含好几个路径。多路径应该用分隔符分开。默认情况下,编译器和JVM查找当前目录。JAR文件按包含Java平台相关的类,所以他们的目录默认放在了class path中        

设置CLASSPATH系统变量
    用下面的命令显示当前的CLASSPATH变量:
        Windows平台(DOS 命令行下)-> C:\> set CLASSPATH
        UNIX平台(Bourne shell下)-> % echo $CLASSPATH
    删除当前CLASSPATH变量内容:
        Windows平台(DOS 命令行下)-> C:\> set CLASSPATH=
        UNIX平台(Bourne shell下)-> % unset CLASSPATH; export CLASSPATH
    设置CLASSPATH变量:
        Windows平台(DOS 命令行下)-> set CLASSPATH=C:\users\jack\java\classes
        UNIX平台(Bourne shell下)-> % CLASSPATH=/home/jack/java/classes; export CLASSPATH
        
        

JAVA Enumeration接口

Enumeration接口中定义了一些方法,通过这些方法可以枚举对象集合中的元素
这种传统接口已被迭代器取代,虽然Enumeration还未被丢弃,但现代代码中很少使用了.
它还使用在诸如Vector和Properties这些传统类所定义的方法中,除此之外,还用在一些API类
例子:
    import java.util.Vector;
    import java.util.Enumeration;

    public class EnumerationTester {

       public static void main(String args[]) {
          Enumeration days;
          Vector dayNames = new Vector();
          dayNames.add("Sunday");
          dayNames.add("Monday");
          dayNames.add("Tuesday");
          dayNames.add("Wednesday");
          dayNames.add("Thursday");
          dayNames.add("Friday");
          dayNames.add("Saturday");
          days = dayNames.elements();
          while (days.hasMoreElements()){
             System.out.println(days.nextElement()); 
          }
       }
    }
    运行结果:
        Sunday
        Monday
        Tuesday
        Wednesday
        Thursday
        Friday
        Saturday

JAVA Bitset类

一个Bitset类创建一种特殊类型的数组来保存位值.BitSet中数组大小会随需要增加.这和位向量(vector of bits)比较类似
方法:
    很多,直接看网址:https://code.ziqiangxuetang.com/java/java-bitset-class.html
例子:
    import java.util.BitSet;

    public class BitSetDemo {
      public static void main(String args[]) {
         BitSet bits1 = new BitSet(16);
         BitSet bits2 = new BitSet(16);
          
         // set some bits
         for(int i=0; i<16; i++) {
            if((i%2) == 0) bits1.set(i);
            if((i%5) != 0) bits2.set(i);
         }
         System.out.println("Initial pattern in bits1: ");
         System.out.println(bits1);
         System.out.println("\nInitial pattern in bits2: ");
         System.out.println(bits2);

         // AND bits
         bits2.and(bits1);
         System.out.println("\nbits2 AND bits1: ");
         System.out.println(bits2);

         // OR bits
         bits2.or(bits1);
         System.out.println("\nbits2 OR bits1: ");
         System.out.println(bits2);

         // XOR bits
         bits2.xor(bits1);
         System.out.println("\nbits2 XOR bits1: ");
         System.out.println(bits2);
      }
    }
    以上实例编译运行结果如下:
    Initial pattern in bits1:
    {0, 2, 4, 6, 8, 10, 12, 14}

    Initial pattern in bits2:
    {1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14}

    bits2 AND bits1:
    {2, 4, 6, 8, 12, 14}

    bits2 OR bits1:
    {0, 2, 4, 6, 8, 10, 12, 14}

    bits2 XOR bits1:
    {}
    

JAVA Vector类

Vector类实现了一个动态数组.和ArrayList相似
    Vector时同步访问的
    Vector包含了许多传统的方法,这些方法不属于集合框架
Vector主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况
方法:
    很多,看网址:https://code.ziqiangxuetang.com/java/java-vector-class.html
例子:
    import java.util.*;

    public class VectorDemo {

       public static void main(String args[]) {
          // initial size is 3, increment is 2
          Vector v = new Vector(3, 2);
          System.out.println("Initial size: " + v.size());
          System.out.println("Initial capacity: " +
          v.capacity());
          v.addElement(new Integer(1));
          v.addElement(new Integer(2));
          v.addElement(new Integer(3));
          v.addElement(new Integer(4));
          System.out.println("Capacity after four additions: " +
              v.capacity());

          v.addElement(new Double(5.45));
          System.out.println("Current capacity: " +
          v.capacity());
          v.addElement(new Double(6.08));
          v.addElement(new Integer(7));
          System.out.println("Current capacity: " +
          v.capacity());
          v.addElement(new Float(9.4));
          v.addElement(new Integer(10));
          System.out.println("Current capacity: " +
          v.capacity());
          v.addElement(new Integer(11));
          v.addElement(new Integer(12));
          System.out.println("First element: " +
             (Integer)v.firstElement());
          System.out.println("Last element: " +
             (Integer)v.lastElement());
          if(v.contains(new Integer(3)))
             System.out.println("Vector contains 3.");
          // enumerate the elements in the vector.
          Enumeration vEnum = v.elements();
          System.out.println("\nElements in vector:");
          while(vEnum.hasMoreElements())
             System.out.print(vEnum.nextElement() + " ");
          System.out.println();
       }
    }
    
    

Java Stack类

栈时Vector的一个子类,它实现了一个标准的后进先出的栈
堆栈只定义了默认构造函数,用来创建一个空栈.堆栈除了包括由Vector定义的所有方法,也定义了自己的一些方法
例子:
    import java.util.*;

    public class StackDemo {

       static void showpush(Stack st, int a) {
          st.push(new Integer(a));
          System.out.println("push(" + a + ")");
          System.out.println("stack: " + st);
       }

       static void showpop(Stack st) {
          System.out.print("pop -> ");
          Integer a = (Integer) st.pop();
          System.out.println(a);
          System.out.println("stack: " + st);
       }

       public static void main(String args[]) {
          Stack st = new Stack();
          System.out.println("stack: " + st);
          showpush(st, 42);
          showpush(st, 66);
          showpush(st, 99);
          showpop(st);
          showpop(st);
          showpop(st);
          try {
             showpop(st);
          } catch (EmptyStackException e) {
             System.out.println("empty stack");
          }
       }
    }
    运行结果:
        stack: [ ]
        push(42)
        stack: [42]
        push(66)
        stack: [42, 66]
        push(99)
        stack: [42, 66, 99]
        pop -> 99
        stack: [42, 66]
        pop -> 66
        stack: [42]
        pop -> 42
        stack: [ ]
        pop -> empty stack
        

JAVA Dictionary 类

Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似
给出键和值,你就可以将值存储在 Dictionary 对象中,一旦该值被存储,就可以通过它的键来获取它.

Dictionary 类已经过时了.在实际开发中,可实现Map 接口 来获取键/值的存储功能

JAVA Map 接口

Map 接口中键和值一一映射.可通过键来获取值
    给定一个键和一个值,你可以将该值存储在一个Map对象. 之后,你可以通过键来访问对应的值。
    当访问的值不存在的时候,方法就会抛出一个NoSuchElementException异常.
    当对象的类型和Map里元素类型不兼容的时候,就会抛出一个 ClassCastException异常。
    当在不允许使用Null对象的Map中使用Null对象,会抛出一个NullPointerException 异常。
    当尝试修改一个只读的Map时,会抛出一个UnsupportedOperationException异常。
方法: https://code.ziqiangxuetang.com/java/java-map-interface.html

例子:
    import java.util.*;

    public class CollectionsDemo {
       public static void main(String[] args) {
          Map m1 = new HashMap(); 
          m1.put("Zara", "8");
          m1.put("Mahnaz", "31");
          m1.put("Ayan", "12");
          m1.put("Daisy", "14");
          System.out.println();
          System.out.println(" Map Elements");
          System.out.print("\t" + m1);
       }
    }
    运行结果:
        Map Elements
            {Mahnaz=31, Ayan=12, Daisy=14, Zara=8}
            

JAVA Hashtable 接口

Hashtable现在集成到了集合框架中,支持同步.
Hashtable在哈希表中存储键/值对,当使用一个哈希表,要指定用作键的对象,以及要链接到该键的值

然后,该键经过哈希处理,所得到的散列码被用作存储在该表中值的索引

方法:https://code.ziqiangxuetang.com/java/java-hashtable-class.html
例子:
    import java.util.*;

    public class HashTableDemo {
       public static void main(String args[]) {
          // Create a hash map
          Hashtable balance = new Hashtable();
          Enumeration names;
          String str;
          double bal;

          balance.put("Zara", new Double(3434.34));
          balance.put("Mahnaz", new Double(123.22));
          balance.put("Ayan", new Double(1378.00));
          balance.put("Daisy", new Double(99.22));
          balance.put("Qadir", new Double(-19.08));

          // Show all balances in hash table.
          names = balance.keys();
          while(names.hasMoreElements()) {
             str = (String) names.nextElement();
             System.out.println(str + ": " +
             balance.get(str));
          }
          System.out.println();
          // Deposit 1,000 into Zara's account
          bal = ((Double)balance.get("Zara")).doubleValue();
          balance.put("Zara", new Double(bal+1000));
          System.out.println("Zara's new balance: " +
          balance.get("Zara"));
       }
    }

Java Properties 接口

Properties 继承于 Hashtable.表示一个持久的属性集.属性列表中每个键及其对应值都是一个字符串
Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值
方法: https://code.ziqiangxuetang.com/java/java-properties-class.html
例子:
    import java.util.*;

    public class PropDemo {

       public static void main(String args[]) {
          Properties capitals = new Properties();
          Set states;
          String str;
          
          capitals.put("Illinois", "Springfield");
          capitals.put("Missouri", "Jefferson City");
          capitals.put("Washington", "Olympia");
          capitals.put("California", "Sacramento");
          capitals.put("Indiana", "Indianapolis");

          // Show all states and capitals in hashtable.
          states = capitals.keySet(); // get set-view of keys
          Iterator itr = states.iterator();
          while(itr.hasNext()) {
             str = (String) itr.next();
             System.out.println("The capital of " +
                str + " is " + capitals.getProperty(str) + ".");
          }
          System.out.println();

          // look for state not in list -- specify default
          str = capitals.getProperty("Florida", "Not Found");
          System.out.println("The capital of Florida is "
              + str + ".");
       }
    }

作者:JcTung