\ckage com.javabase.test;

/*
 *                                         Java总结

jdk:Java的开发环境与部署环境
jre:Java的运行环境

Java数据类型分为:基本类型和引用类型
    基本类型:①整数类型 byte   short   int   long    默认值: 0
          ②字符类型 char                        默认值: 空格
          ③浮点类型 float  double                默认值: 0.0
          ④布尔类型 boolean                        默认值: false
    引用类型:①类
             ②接口
             ③数组
             ④ null 类型
    Java中除基本类型之外的类型都被称为引用类型,默认值为: null

八大基本数据类型: 
byte    字节型            1个字节            8位            -2^7~2^7-1
short    短整型            2个字节            16位        -2^15~2^15-1
int        整型                4个字节            32位        -2^31~2^31-1    默认
long    长整型            8个字节            64位        -2^63~2^63-1    使用时在数据最后加    L/l
float    单精度浮点型        4个字节            32位                使用时在数据最后加    F/f
double    双精度浮点型        8个字节            64位                默认
char    字符型            2个字节            16位                0~65535
boolean    布尔型            1个字节            8位            false true
    可以给 char 型的变量直接赋 int 型的值,取值范围是0~65535,但是不能直接赋整数的变量。
    整数与小数(整型与浮点型)运算得到的结果是一个浮点型的数。
    整数与整数(或整型变量)运算得到的是一个 int 型的值,但 long 与其他整数进行运算得到的是一个 long 型的值。

数据类型转换: 字节小的可以直接转换成字节大的
强制类型转换: 需要的数据类型 变量名=(需要的数据类型)要转换的数据

Java运算符:
    算术运算符: + - * / %  
    赋值运算符: = += -= *= /= %= &= |= ^= <<= >>= >>>=
    位运算符: &(按位与) |(按位或) ~(按位非) ^(按位异或) <<(左移运算符) >>(右移运算符) >>>(无符号右移运算符)
    比较运算符: > < >= <= == !=
    逻辑运算符: &&(逻辑与):两者为真才为真,只要一个为假,结果则为假 运算时如第一个运算结果为假,则不再进行后续运算 得到结果为假
                ||(逻辑或):一个为真,结果就为真    运算时,如第一个运算结果为真,则后续运算不再进行 得到结果为真
                !(逻辑非): 把假变真,把真变假
                &(不短路与)
                |(不短路或)
                ^(异或):当两个结果不同时才返回 true,当两个结果相同时则返回 false;
    自增自减: ++(自增): 前++: 先自增再运算  后++: 先运算再自增
              --(自减): 前--: 先自减再运算  后--: 先运算再自减
    三目运算符: (先对逻辑表达式求值) ? (为真返回结果) : (为假返回结果);

&和&&的区别:
    单&时,左边无论真假,右边都进行运算;
    双&时,如果左边为真,右边参与运算,如果左边为假,那么右边不参与运算。
    |和||的区别同理,双或时,左边为真,右边不参与运算。

==与=号的区别:
    ==判断等号两边是否相等,==号判断八大基本数据类型的时候是比较值是否相等,比较非八大基本数据类型的时候是比较引用地址是否相等
    =号是赋值运算符,是将=号右边的数值赋值给=号左边;

条件语句: 
    ① if(条件表达式){
        代码块;
       }
    ② if 嵌套:if(条件表达式1){
                if(条件表达式2){
                 if(条件表达式n){
                   代码块;
                 }
                }
               }
    ③ if(条件表达式){
        代码块1;
       }else {
         代码块2;
       }
    ④ if(条件表达式1){
        代码块1;
       }else if(条件表达式2){
        代码块2;
       }else if(条件表示式3){
        代码块3;
       }else{
        代码块4;
       }
    ⑤ switch(值或一个变量){
        case 值1:
            执行内容;
            break;
        case 值2:
            执行内容;
            break;
        default:
            默认内容;
            break;
    } 
    switch 条件中可以使用的类型:byte,short,int,char,枚举,jdk1.7后可以使用 String;

循环控制语句:
    for 循环: for (初值表达式;终止条件;步长表达式){
                语句块;
             }
    while 循环: while (循环条件){
                循环体;
                迭代语句;
               }
    do while 循环: do{
                    循环体;
                    迭代语句;
                  }while (循环条件);
    do while 特点是条件无论是否满足,循环体至少被执行一次。
    
    foreach 循环: Java1.5后才出现
                使用 foreach 循环遍历数组和集合元素时,无须获得数组和集合长度,无须根据索引来访问数组元素和集合元素

foreach 循环与普通循环的区别:无须循环条件,无须循环迭代语句;

九九乘法表:
    for (int i=1;i<=9 ;i++ )
    {
        for (int j=1;j<=i ;j++ )
        {
            System.out.print(j+"*"+i+"="+i*j+"\t");
        }
        System.out.println();
    }

百钱买百鸡:100元钱买100只鸡,其中公鸡5元一只,母鸡3元一只,小鸡3只一元,问有多少方案?
    for (int cock=1;cock<=20 ;cock++ )
    {
        for (int hen=1;hen<=33 ;hen++ )
        {
            for (int chick=3;chick<=99 ;chick+=3 )
            {
                if (cock*5+hen*3+chick/3==100)    //百钱
                {
                    if (cock+hen+chick==100)    //百鸡
                    {
                        System.out.println("公鸡: "+cock+"\t母鸡: "+hen+"\t雏鸡: "+chick);
                    }
                }
            }
        }
    }

斐波那契数列:0、1、1、2、3、5、8、13、21、34....
        ①int[] arr = new int[20];  
        arr[0] = arr[1] = 1;  
        for (int i = 2; i < arr.length; i++) {  
            arr[i] = arr[i - 1] + arr[i - 2];  
        }  
        System.out.println("斐波那契数列的前20项如下所示:");  
        for (int i = 0; i < arr.length; i++) {  
            System.out.print(arr[i] + "\t");  
        } 
        ②int s=1;
        int k=1;
        int temp=0;
        for (int i=0; i<20;i++ ){
            temp=s+k;
            s=k;
            k=temp;
        }
        System.out.println("斐波那契数列的前20项"+temp);  

1~100以内的质数:
    for(int i=1;i<=100;i++) {
      for(int j=2;j<=i;j++){
       if(i%j==0)//素数是指除了1和自身外不能被任何数整除的数,因此遍历每一个小于i大于2的整数j
       //如果i能够被j整除
         if(i==j)
           //如果当i等于j的时候则满足i是素数的条件,即只能被1(j是从2计数的)和自身整除,因此i是素数
           System.out.println(i +"是素数");
         else
           //在如果存在一个小于i大于2的整数j可以整除i,则i必不是素数,因此break操作.
           break;//如果i可以被j整除且j不等于i,则跳出循环
      }
    }

打印正三角形:
    for(int i=1;i<=10;i++){
        for(int j=10-i;j>0;j--){
            System.out.print(" ");
        }
        for(int j=1;j<=2*i-1;j++){
            System.out.print("*");
        }
        System.out.println();
    }

break:跳出当前循环语句(结束当前循环语句);
continue:结束本次循环,继续执行下一次循环,而不是直接退出,
        当使用 continue 时,此后的代码将无法运行,可以控制循环何时结束;
return:返回方法,后面不能再存在语句

数组:同一种数据类型的集合(下标从0开始~length-1结束)
    格式:数据类型[] 数组名=new 数据类型[数组长度];
    获取数组长度:    数组名.length;
    获取数组元素:    数组名[下标号];
    数组遍历与数组增删查改
二维数组:
    格式:数据类型[][] 数组名=new 数据类型[行数][列数];
         数据类型[][] 数组名={{},{},{}};
冒泡排序法:把小的泡冒到上面,大的沉到下面,最值在中间与其他值进行交换
    for (int i=0;i<array.length-1 ;i++ )
    {
        for (int j=0;j<array.length-i-1 ;j++ )
        {
            if (array[j]>array[j+1])
            {
                int temp=array[j];
                array[j]=array[j+1];
                array[j+1]=temp;
            }
        }
    }
    
选择排序法:
    for (int i=0;i<array.length-1 ;i++ )
    {
        for (int j=i+1;j<array.length ;j++ )
        {
            if (array[i]>array[j])
            {
                int temp=array[i];
                array[i]=array[j];
                array[j]=temp;
            }
        }
    }

从前往后删除多个:
    for (int i=0;i<count ;i++ )
    {
        if (del==array[i][0])
        {
            for (int j=i;j<count-1 ;j++ )
            {
                for (int k=0;k<array[i].length ;k++ )
                {
                    array[i][j]=array[i+1][j];
                }
            }
            count--;
            i--;
        }
    }

------------------------------------- 面 向 对 象 --------------------------------------
数据结构
    目的:加强类与对象的内存分配理解、加强操作能力、理解数据结构。
    数据:计算机化的信息。
    数据元素:数据的基本单位,即数据集中的个体,节点、记录
    数据项:有独立含义的数据,数据域、字段
    数据对象:具有相同特性的数据元素的集合。
    
    结构:数据元素之间的关系
    数据结构:带有结构的数据对象
    线性结构:各数据元素之间的逻辑可以用一个线性序列简单的表达出现,反之为非线性结构
    线性结构包括:线性表(数组,链式表(单链,双链))、队列,栈
    非线性结构:树(只有一个根结点)、图
    栈:限定只能从表一端进行插入和删除的线性表
        特点:First In Last Out(FILO) 先进后出
    队列:限定只能在表的一端进行插入运算,在表的另一端进行删除运算的线性表
        特点:First In First Out(FIFO) 先进先出

数组与链表的区别:
    ①存储空间的区别:数组是静态分配内存空间的,所有元素时存放在一组地址连续的存储单元中,一旦分配,不可更改,也就不便于扩展,
    数据元素在数组中的顺序号可确定它在存储单元中的位置,因此顺序中不需要指针域,而链表是动态分配内存空间,存储空间是不确定的。
    ②数组便于查找和修改(因为是下标定位),但是不利于插入和删除(删除的时候数组要一个一个的移动,数据的移动量过大),也不便于扩充因为它是连续的地址,
    是静态的存储结构,而链表不便于查找与修改(需要从链头找到链尾数组量过大),但便于插入,删除且速度快(因为可以指针直接指向,删除的时候直接脱链就可以了)。

链表是不是二叉树?
    是,链表是左斜树或右斜树中的一种。
    左斜树:从根节点开始,所有节点都一直往左走。
    右斜树:从根节点开始,所有节点都一直往右走。

树是N(N>0)个结点的有限集合,在一棵非空有限集合中,有且仅有一个特定的结点称为树的根结点,根节点之下可以有0个以上的子节点(也可以没有),而各子节点称为此树的子树。
    根结点、叶子结点(终端结点)、父结点、兄弟结点、结点度(分支度)、树的度(树的分支度)、阶层、高度(深度)、祖先、林、
    根结点:一个树当中没有父结点的结点。
    叶子结点(终端结点):一棵树中没有子结点的结点。
    非终端结点:除叶子结点以外的所有结点。
    兄弟结点:同一个父结点的所有结点。
    结点度(分支度):每一个结点拥有结点的个数。
    树的度(树的分支度):一棵树中最大的结点。
    阶层:结点特性值:将结点值设为1,其子结点的值为2。
    高度和深度:一棵树中最大阶层值。
    祖先:由某个结点X到根结点之路径上的所有结点。

二叉树也被称为二次树或二分树,是树的一种,二叉树的结点最多只有两个,因此二叉树的结点度最大值是2。
二叉树的定义:    由有限个结点所构成之集合,此集合可以为空。
                二叉树的结点可以分为两棵子树,称左子树和右子树。同时左右子树也是二叉树。
    在二叉树的第I层最多有2的I减1次方个结点。(I>=1)。2^(I-1)
    高度为K的二叉树中最多有2的K次方减1个结点。2^K-1
    
    满二叉树:树中所有结点均在同一阶层而其它非终端结点度均为“2”,且树的高度是K,其结点为2的K次方减1。2^k-1
    完全二叉树:一棵树中扣除最大阶层,那层后一满二叉树,且阶层最大层的阶层均向左对齐。此树称为完全二叉树。
    一棵树如果是满二叉树,那么它一定是完全二叉树,一棵树如果是完全二叉树,它不一定是满二叉树。

    (小左大右)
二叉树的遍历:①先序:根 左 右 若二叉树非空,则访问根结点,按先序遍历左子树,再遍历右子树。
             ②中序:左 根 右 若二叉树非空,按中序遍历左子树,再访问根结点,再按中序遍历右子树。
             ③后序:左 右 根 若二叉树非空,按后序遍历左子树,再遍历右子树,再访问根结点。

二叉树的删除:①无左无右:分为: 根结点 非根结点,但是是叶子结点(分为:左叶子 右叶子)
             ②有左无右:分为: 根结点 非根结点(分为:左结点 右结点)
             ③有右无左:分为: 根结点 非根结点(分为:左结点 右结点)
             ④有左有右:分为: 根结点 非根结点(分为:左结点 右结点)(判断是要上移左结点的最右边或右结点的最左边)

树与二叉树的比较:
    二叉树可以为空,而树不可以(至少要有根结点)。
    二叉树的子树有顺序关系,而树没有。
    二叉树的度必为(0 ,1, 2)而树的结点度可以大于2。


定义Java中的方法:
    访问修饰符  返回值类型   方法名(参数列表){
        方法体;
    }
方法的优点: (方法的命名以小写字母开头,遵循驼峰命名规则)
        ①使程序变得更简短而清晰
        ②有利于程序的维护
        ③可以提高程序开发的效率
        ④提高了代码的复用性
    
class 类名{
    属性(成员变量): 数据类型 属性名=初始值;
    行为(方法);
}
根据类来创建对象:
    语法: 类型(数据类型)变量名=new 类名();        //类名与变量名不能一致
类的对象的调用: 
    对象的变量名.方法名([参数]);
在同一个Java文件中可以定义多个类,但是只有一个类可以用 public 修饰,并且类名要与 public 所修饰的类名保持一致。

成员变量(实例变量、全局变量):成员变量定义在类中,在整个类中都可以被访问
                     成员变量有默认初始化值,每 new 一次就开辟一次内存空间
                    成员变量随着对象的建立而建立,存在于对象所在的堆内存中
局部变量:局部变量只定义在局部范围内(函数内,语句内等)
         局部变量存在于栈内存中,作用范围结束,变量空间将自动释放
         局部变量没有默认初始化值
类变量:使用 static 修饰,只开辟一次内存,定义在类中
       伴随类的存在而存在,释放内存较晚
成员变量与类变量的区别:
    ①存放位置: 类变量随着类的加载而存在于方法区中,实例变量随着对象的建立而存在于堆内存中、
    ②生命周期: 类变量生命周期最长,随着类的消失而消失,实例变量生命周期随着对象的消失而消失

静态变量和实例变量的区别:
    ①在语法定义上的区别: 静态变量前要加static关键字,而实例变量前则不加。
    ②在程序运行时的区别: 实例变量属于某个对象的属性,必须创建了实例变量,其中的实例变量才会被分配空间,才能使用这个实例变量。
                   静态变量不属于某个实例对象,而属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会分配空间,静态变量就可以被使用了。
    实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。


面向对象开发经过 OOA(面向对象分析)、OOD(面向对象设计)和 OOP(面向对象编程) 三个阶段
面向对象(OO): 将功能封装进对象,强调具备了功能的对象
    类与对象的关系: 类是对象的抽象,对象是类的实例
    类的成员包括: 属性(变量)和行为(方法)
    用来描述对象的动态特征(行为)的一个动作序列是对象的方法而非属性,类的方法描述了该类的对象的行为
    使用对象的属性形式是: 对象.属性名
    使用对象的方法形式是: 对象.方法名()
面向对象的三大(四大)特征: 封装、继承、多态(抽象)
面向对象的思维:
    作为面向对象的思维来说,当考虑问题时,不应该再考虑第一步干嘛,第二步干嘛,而应该首先考虑,作为这个问题来说,
    在这个问题里,应该有哪些类哪些对象;然后再考虑这些类和对象,每一种类和每一种对象,应该具有哪些属性和方法;
    再考虑类与类之间具备了什么样的关系。

Java中参数传递:
    ①传值:方法调用时,实参把它的值传递给对应的形参,方法执行中形参值的改变不影响实参的值。(八大基本类型)
    ②传引用(传址):方法调用时,实参的引用(地址,而不是参数的值)被传递给方法中相对应的形参,
                在方法执行中,对形参的操作实际上就是对实参的操作,方法执行中形参值的改变将会影响实参的值。(非八大基本类型,数组,对象)

权限修饰符: public 公共的 > protected 受保护的 > default 默认的、缺省的、友元 > private 私有的
            所有类、所有包 > 本类、本包、子类  > 本包、本类 (包修饰符)        > 本类

 static 静态修饰符
    ① static 修饰的方法是静态方法,静态方法不能直接访问非静态方法,静态方法可以直接访问静态方法和静态属性
    ②非静态方法任何时候都可以直接访问静态方法和静态属性
    ③静态的方法和属性可以直接用类名.访问(不同类中)
    ④ static 修饰的方法和属性内存只分配一次,在内装载器中开辟内存
    ⑤ static 修饰的变量是属于类而不属于对象, static 修饰的内容被对象所共享
    ⑥ static 不能以任何形式去访问 this 和 super
    ⑦如果想在第一时间执行加载某段代码,可以使用静态代码块(静态代码块只会执行一次)
    ⑧ main 方法是程序的入口,必须使用 static 修饰
    ⑨ static 不能修饰类(内部类可以使用 static 修饰),不能修饰构造方法
静态是在内装载器中开辟内存,而非静态是在 new 一个对象的时候才在堆内存中开辟内存
静态如果想要调用非静态,必须要先 new 一个对象,然后使用对象调用
当成员被静态修饰符修饰时,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用,类名.静态成员

是否可以从一个static方法内部发出对非static方法的调用?
    不可以,因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。

重载( overload ): 在同一个类中,方法名相同,参数列表不同,与返回值及权限修饰符无关
    参数列表不同的情况: ①参数个数的不同
                  ②参数个数相同,但参数类型不同
                  ③参数个数与参数类型都相同时,参数类型的顺序不同

封装(private): 把一个对象的属性私有化,同时提供一些可以被外界访问的属性和方法, setter() 方法和 getter() 方法,this代表当前对象
    封装就是使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。
    隐藏对象的内部实现细节
    private double money;
    public void setMoney(double money){
        this.money = money;
    }
    public double getMoney(){
        return this.money;
    }
    封装的好处: ① 类内部的结构可以自由的修改
                ② 可以对成员进行更精确的控制
                ③ 隐藏信息,实现细节

继承(extends): 子类继承父类除私有化修饰的属性和方法        具有 "什么什么是一种什么" 二者之间就存在继承关系
      关键字: extends    (子类其实也继承了父类私有化修饰的属性和方法,但是子类没有访问父类中私有化修饰的属性和方法的权限)
    父类(超类、基类)(superclass)        子类(派生类)        object 类是所有类的父类(祖宗类)
    继承的特点: Java只支持单一继承,不支持多继承        一个子类只能有一个直接父类,一个父类可以有多个子类
    继承的优点: 减少了代码的冗余,提高了代码的复用性
    继承的目的: 让子类可以使用父类除私有化修饰的属性和方法

重写( override / overwrite )(方法覆盖): 建立在继承的基础之上,子类与父类拥有相同的方法名,相同的参数列表,子类返回值权限小于等于父类返回值权限,并且子类方法访问权限大于等于父类方法访问权限
    重写遵循 两同两小一大 原则:
        两同: ①方法名相同 
              ②形参列表相同
        两小: ①子类方法返回值类型应比父类方法返回值类型更小或相等
              ②子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等
        一大: 子类方法的访问权限应比父类方法的访问权限更大或相等
    为何需要重写: 当父类方法不能满足子类的需求的时候,就需要重写父类方法

重载与重写的异同: 
    同:都有相同的方法名
    异:①重载是在同一个类中,而重写是在继承的基础上,在子父类中
       ②重载是方法的参数列表不同,而重写是子父类中方法参数列表一致
       ③重载与返回值无关,而重写是子类方法返回值类型比父类方法返回值类型更小或相等
       ④重载与权限修饰符无关,而重写是子类方法的访问权限比父类方法的访问权限更大或相等
Overload 和 Override 的区别。Overloaded的方法是否可以改变返回值的类型?
    Overload是重载的意思,Override是覆盖的意思,也就是重写。
    重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。
    重写Override表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,
    子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,
    子类方法的访问权限只能比父类的更大,不能更小。
Override 可以翻译为覆盖,它是覆盖了一个方法并且对其重写,以求达到不同的作用,在覆盖是要注意:
    1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
    2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
    3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
    4、被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
Overload对我们来说可能比较熟悉,可以翻译为重载,是指我们可以定义一些名称相同的方法,通过定义不同的输入参数来区分这些方法,
    然后再调用时,VM就会根据不同的参数样式,来选择合适的方法执行,在使用重载要注意:
    1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样);
    2、不能通过访问权限、返回类型、抛出的异常进行重载;
    3、方法的异常类型和数目不会对重载造成影响;
    4、对于继承来说,如果某一方法在父类中是访问权限是private,那么就不能在子类对其进行重载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。
    Overloaded的方法可以改变返回值的类型,因为重载与返回值无关。
    
构造方法(构造器)( constructor ):
    ①方法名与类名一样
    ②构造方法无返回类型,并且在new一个对象的时候,由JVM自动调用
    ③构造方法同样遵从方法重载的原则
    ④在构造方法中调用另一个构造方法, this 语句必须放在第一行,this(要调用的构造方法的参数)
    ⑤提前初始化类中的属性和方法
    ⑥如果程序在类中有非空构造方法,那么JVM不会自动生成空构造方法,否则会自动生成一个空构造方法
    ⑦ new 一个对象的时候,从这个对象开始到 object ,默认是最子类查找空构造方法直到 object ,
      途中如果有一个类缺少空构造方法,则编译不通过。
      查找是从最子类查找到最父类
      执行是从最父类执行到最子类
    ⑧ super 可调用父类的非空构造方法 super(要调用构造方法参数);要放在第一行。
      super 调用父类的属性和方法
    ⑨不能使用 static 修饰构造方法

    在同一个构造方法中,不能同时出现使用 super 与 this 调用构造函数,因为在构造函数中,
    使用 super 与 this 调用构造方法,语句都需要放在程序的第一行!!!

构造器Constructor是否可以被Override?
    构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload。

多态(动态绑定、迟绑定)( Polymorphism ): 使用 instanceof 判断前一个对象是否为后一个类的实例
    指在执行期间(而非编译期)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
    多态是建立在继承的基础之上,在继承机制的允许下,可以将某个对象看成其所属类,也可以看成其超类,这样就可以将
     多个对象看成同一个类,当子类重写父类的方法,由同一个对象调用,产生不同的行为,这种现象就叫多态。
    把子类的实例赋给父类的引用,父类的引用指向子类的实例。
多态存在的三个必要条件:①要有继承
                ②要有重写
                ③父类引用指向子类对象
    三个条件满足,当你调用父类里被重写的方法时,实际中 new 的哪个子类对象就调用哪个子类对象的方法。
非静态方法: 父有子有,调子类方法;父有子无,调父类方法;
静态方法:父有子有,调父类方法;父有子无,调父类方法;
属性:父有子有,调父类方法;父有子无,调父类方法;
父无子有 编译失败
    引用类型变量 instanceof 类(接口)
instanceof 运算符前面操作数的编译时,类型要么与后面的类相同,要么与后面的类具有父子继承关系,否则会引起编译错误。
instanceof 运算符的作用是: 在进行强制类型转换之前,首先判断前一个对象是否为后一个类的实例,是否可以成功转换,从而保证代码更加健壮。
 
final:
    ①可以修饰类,但是不能被继承(没有子类, final 类中的方法默认是 final 的)
    ②可以修饰方法,但是不能被重写,可以被继承
    ③可以修饰属性,修饰的属性值不能改变,是一个常量(常量命名规则,字母全部是大写,两个单词以上的用"_"连接)
    ④可以修饰参数,修饰的参数也不能改变
    ⑤不能修饰构造方法。

使用final 关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
    使用final 关键字修饰一个变量时,是引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。

抽象类:abstract 抽象的就是看不懂的
抽象类的含义:逆着关系由下向上(从子类到父类),类逐渐变得通用,变得抽象,越往上越通用、越抽象,越往下越具体实用
注意事项:
    ①抽象方法所在的类就是抽象类
    ②抽象方法:没有方法体的方法就叫做抽象方法,抽象方法要用 abstract 修饰
    ③抽象类不能 new(不能实例化)
    ④抽象类可以被继承,但是子类要有两种情况(1.重写父类的所有抽象方法 2.要么子类用 abstract 修饰)
    ⑤抽象类可以继承抽象类,也可以继承普通类,普通类也可以继承抽象类
    ⑥抽象类里面可以有变量、常量、普通方法、构造方法
    ⑦抽象方法不能用 private 修饰(私有化)
    ⑧抽象方法不能用 static 修饰
    ⑨抽象类也不能用 final 修饰
    ⑩被 abstract 修饰的东西不能与 native 同时使用,也不能与 synchronized 同时使用
    ⑾抽象类中的方法要被使用,必须由子类重写其所有的抽象方法后,建立子类对象调用
    如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类

接口:interface    扩展了程序的功能
    概念:和类是同一种结构,但是比抽象类更加抽象
    (可以把接口理解为一个纯的抽象类,接口里所有的方法都是抽象方法)
    注意事项:
    1.接口里不能有构造方法、不能有普通方法、可以有抽象方法
    2.接口里抽象方法可以加 abstract 修饰,可以不加,默认会是 public 、 abstract 修饰
    3.接口里面的抽象方法不能用 protected 和 private 修饰,可以用 public 修饰
    4.接口里可以有变量、常量、静态常量,编译后所有属性都是静态常量(默认为:public static final 修饰)
    5.接口可以用来弥补Java里面类单一继承的缺陷
    6.接口可以继承接口,并且可以继承多个接口(接口只能继承接口,不能继承类,继承多个接口中间用","分隔)
    7.类可以实现多个接口(实现接口后必须要重写所有的抽象方法),实现多个接口的时候用","分隔
    8.接口不能实例化(不能 new)
    9.实现接口关键字:implements
    10.类实现接口语法是: class 类名 implements 接口名

    类可以继承一个类的同时实现多个接口。

    为什么Java不支持多继承却支持多实现?
    因为多继承的方法,都存在方法体,当子类重写父类的方法时,会使JVM绑定规则变得更复杂,
    而多实现的方法,不存在方法体,可以由子类任意定义,只需重写一次,故而不会增加JVM的绑定机制和复杂度。

抽象类与接口的区别: 
    同: ①都是上层的抽象层
        ②都不能实例化
        ③都能包含抽象的方法,这些抽象的方法用于描述类具备的功能,但是不必提供具体的实现。
    异: ①在抽象类中可以写非抽象的方法,从而避免在子类中重复书写他们,这样可以提高代码的复用性,这是抽象的优势;接口中只能有抽象的方法。
        ②一个类只能继承一个直接父类,这个父类可以是具体的类也可以是抽象类;但是一个类可以实现多个接口。
接口可以继承接口。抽象类可以实现(implements)接口,抽象类可以继承具体类。抽象类中可以有静态的main方法。

abstract class类中定义抽象方法必须在具体(Concrete)子类中实现
接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的
接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final

抽象类与接口两者的语法区别:
    1.抽象类可以有构造方法,接口中不能有构造方法。
    2.抽象类中可以有普通成员变量,接口中没有普通成员变量
    3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
    4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
    5. 抽象类中可以包含静态方法,接口中不能包含静态方法
    6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
    7. 一个类可以实现多个接口,但只能继承一个抽象类。
    两者在应用上的区别:
    接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。
    而抽象类在代码实现方面发挥作用,可以实现代码的重用.
------------------------------------------------------------------------------------------------------------------------------------------
                                                    面向对象试卷知识点解析
    1. Java是一种面向对象的语言,其核心概念即 类和对象,采用面向对象的设计思想可以利用封装、继承和多态实现代码的复用。
    2. 所谓对象就是真实世界中的实体,对象与实体是一一对应的,类是具备某些共同特征的实体的集合,它是一种抽象的概念。
       类是一种抽象的数据类型,它是对所具有相同特征实体的抽象,类与对象的关系:类是对象的抽象,对象是类的实例。
    3. 在Java中,一组有相同属性、共同行为和共同关系的对象的抽象称为类。
    4. 类是对象的抽象,对象是类的实例。
    5. 类的实例是对象,类是对现实世界中客观事物的抽象。
    6. 用来描叙对象的动态特征的一个动作序列是对象的方法而非属性。类的方法描述了该类的对象的行为。
    7. 使用对象的属性形式是 对象.属性,使用对象的方法形式是对象.方法名();属性是对象所拥有的静态特征,方法是对象拥有的动态特征。
    8. 类的成员包括属性和行为。
    9. 在Java中,定义一个类使用关键字 class ,而创建类的对象则使用 new 关键字。类可以是 private 的也可以是 public 的。
    10.方法定义后,声明为 public 的方法,可以被本类或其他类调用,而如果被本来的其他方法定义,不一定非要声明为 public 。
    11.方法的调用,在本类中两个普通方法的相互调用,可以直接写方法名即可,也可以通过 this 关键字调用。
    12.对象的方法除在main()中可以调用外,也可以在其他位置调用。
    13. Java中类的创建方式: 访问修饰符  class  类名{}
    14. Java中无参方法的创建方式: 访问修饰符 返回值类型 方法名(){}
    15.被 static 修饰的变量叫静态变量或类变量,被 static 修饰的成员变量和成员方法独立于该类的任何对象,也就是说,
       它不依赖类特定的实例,被类的所有实例共享,只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内找到它们,
       因此, static 对象可以在它的任何对象创建之前访问,无需引用任何对象,用 public 修饰的 static 成员变量和成员方法本质是全局变量和全局方法,
       当声明它类的对象的时候,不生成 static 变量的副本,而是类的所有实例共享同一个 static 变量。
    16. 在类中,被 static 修饰的方法叫静态方法,也叫类方法。类方法可以被类名直接调用,也可以被实例调用,在静态方法中,
       只能调用静态方法,不可以直接调用非静态方法。
    17.使用 static 修饰的成员称为静态成员或类成员,可以使用类名直接访问,而未使用 static 修饰的成员为实例成员,只能使用类的对象访问。
    18.在静态方法中,类的静态成员变量可以直接使用,而类的实例变量不能直接使用,必须先创建对象,再使用对象访问。
    19.实例方法可以直接调用本类的方法,不能直接调用父类的实例方法和类方法,也不能调用其他类的实例方法。
    20.成员变量可以和成员方法中的局部变量同名,在成员变量前加 this ,使用 this 访问该成员变量。
    21.一个成员方法中的局部变量不能在另一个成员方法中直接访问。
    22.全局静态变量在函数调用过程中更改,离开函数后更改任然有效。
    23.构造方法名必须和类名一致,并且没有返回值,并不是 void ,构造方法是在创建对象时调用,
       对其进行初始化工作,构造方法可以在一个类中出现多次,即 重载。
    24.如果类中显示定义了一个或多个构造方法,并且所有的构造方法都带参数,那么这个类就失去了默认的空构造方法。
    25.实现封装需要通过 private ,在开发中,往往将属性私有化,这些使用 private 修饰的属性或方法,
       只能在本类访问,其他类中不可直接调用,而且构造方法也可以封装。
    26.在类中,私有方法(private)是不可以被其他类直接访问的,访问范围仅限本类。
    27.Java封装的三个步骤:设置 private 属性,生成 get、set 方法对属性进行读写。
    28.封装可增强数据的访问限制,降低各模块之间的依赖程度,也可以提高代码的复用程度,
       增强程序的可维护性,与提高性能无关。
    29.进行程序设计时,应尽量避免一个模块直接操作和访问另一个模块的数据,模块设计追求 高内聚、低耦合。
       高内聚:尽可能把模块的内部数据、功能实现细节隐藏在模块内部独立完成,不允许外部直接干预。
       低耦合:仅暴露少量的方法给外部使用。
    30. String 类型的对象是不能被改变的。
    31. return 语句之后不能再存在其他语句,否则会引起编译错误。
    32.常量使用 final 关键字修饰,其值不能被修改,如果修改了,编译器将不能通过。
    33. abstract 修饰的类是抽象类,抽象类无法实例化,但是可以被继承,自身的方法是可以重载的。
    34.多重继承中,初始化顺序为:父类属性-->父类构造方法-->子类属性-->子类构造方法。
    35.抽象类实现接口时,可以不重写接口内的方法。
    36.接口用于描述系统对外提供的所有服务,因此接口中的成员常量和方法都必须是公开(public)类型的,
       确保外部使用者能访问它们;接口仅仅描述系统能做什么,但不知如何去做,所以接口中的方法都是抽象(abstract)方法;
       接口不涉及任何与实例相关的细节,因此接口没有构造方法,不能实例化,没有实例变量,只有静态(static)常量(final)。
    37.Java可以通过实现接口,间接实现多重继承。
    38.接口不能被实例化,可以有多个实现类,实现类实现接口必须实现接口方法,接口可以被接口继承。
    39.Java中,面向接口编程的好处有:更加抽象,更加面向对象,提高编程的灵活性,提高可维护性,降低耦合。
    40.在Java中,父类对象转换为子类类型是不安全的,子类对象转换为父类类型是安全的。
    41.在Java中,如果被重载和重写的方法相同,那么重载的方法和重写的方法不可以放在相同的类中。
    42.在Java中,如果被重载和重写的方法不同,那么重载的方法和重写的方法可以放在相同的类中。
    43.在Java中,如果被重载和重写的方法不同,那么重载的方法和重写的方法可以放在不同的类中。
    44.创建包的语句是 package ,该语句必须放在程序的第一行。
    45.Java中方法绑定分为: 编译时刻绑定、运行时刻绑定。
    46.在编译时刻绑定的方法有: 静态方法、 private 修饰的方法、 final 修饰的方法
-------------------------------------------------------------------------------------------------------------------------------------------------
API(application program interface): 应用程序编程接口

                            java.lang.*;

八大基本类型对象八大基本包装类: byte   short   int   long   float   double   char    boolean
                    Byte   Short     Integer Long   Float   Double Character Boolean
Integer 与 int的区别:
    int是java提供的8种原始数据类型之一。Java为每个原始类型提供了封装类,Integer是java为int提供的封装类。
    int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况.
    int不适合作为web层的表单数据的类型
    
==号与equals的区别: 
    == 比较八大基本类型是比较值是否相等,比较非八大基本类型的地址是否相等 ,
    equals 是比较非八大基本类型的地址是否相等,但是equals可以通过重写从而满足比较非八大基本类型的值是否相等的要求
重写equals() 方法 以满足判断非八大基本类型值是否相等的要求
重写toString() 方法 利用三目运算符 返回需要得到的信息
==号与equals方法的区别:
    ==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,
    要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。
    equals方法是用于比较两个独立对象的内容是否相同。它比较的是两个对象是独立的。

常量池: 常量池在编译期遇见String 字符串时,它会检查该池内是否已经存在相同的String 字符串,如果找到,
就把新变量的引用指向现有的字符串对象,不创建任何新的String 常量对象,没找到再创建新的。
所以对一个字符串对象的任何修改,都会产生一个新的字符串对象,原来的依然存在,等待垃圾回收。

变长参数: ...  在最后一个形参的类型后增加三点(...),表明该形参可以接受多个参数值。
    个数可变的形参只能处于形参列表的最后,一个方法中最多只能包含一个个数可变的形参。
个数可变的形参本质就是一个数组类型的形参,因此调用包含个数可变形参的方法时,
    该个数可变的形参既可以传入多个参数,也可以传入一个数组。
    当不明确参数个数的时候采用变长参数。

克隆: 三原则: ①必须 implements Cloneable 
            ②必须重写
            ③调用的时候要捕获异常
浅克隆:对象内部是八大基本类型和String ,原对象和克隆出的对象是两个不同的对象,改变原对象数据,并不会改变克隆后对象的值。
深克隆:对象内部是自定义类型,改变的是地址,改变原对象数据,会影响到克隆后的数据的值。

写clone()方法时,通常都有一行代码,是什么?
clone()有缺省行为,代码是: super.clone();因为首先要把父类中的成员复制到位,然后才是复制本身的成员。

String 和 StringBuffer 的区别: 
String 类提供了数值不可改变的字符串,而StringBuffer 类提供的字符串可以进行修改。
String 类覆盖了equals方法和hashCode方法,而 StringBuffer 没有覆盖equals方法和hashCode方法,
所以,将 StringBuffer 对象存储进Java集合类中时会出现问题

--------------------------------- 异常处理: Exception --------------------------------
异常: 运行期 出现的错误。程序中出现不正常的情况。
错误是指在程序运行的过程中,发生的一些异常事件(如: 除0溢出,数组下标越界,所要读取的文件不存在等);
抛出(throw)异常:Java程序的执行过程中如出现异常事件,可以生成一个异常类对象,该异常对象封装了异常事件的信息并提交给Java运行时的系统。
捕获(catch)异常:当Java运行时系统接收到异常对象时,会寻找能处理这一异常的代码,并把当前异常对象交给其处理。
Throwable : 可被抛出的  Java语言中所有错误或异常的超类。
Error: 严重问题:Error 我们不处理。这种问题一般都是很严重的,由java虚拟机生成并抛出,比如说内存溢出、动态链接失败、虚拟机错误等。
Exception: 编译期异常(检查性异常): 必须进行处理的,因为你不处理,编译就不能通过
RuntimeException: 运行期异常(非检查性异常):RuntimeException    这种问题我们可以不处理,因为是你的问题,而且这个问题出现肯定是我们的代码不够严谨,需要修正代码的。

异常的分类:
Error: 称为错误-由java虚拟机生成并抛出,比如说内存溢出、动态链接失败、虚拟机错误等。程序对其不做处理
Exception: 所有异常类的父类,其子类对应了各种各样可能出现的异常事件,一般需要用户显示的声明或捕获。
RuntimeException: 一类特殊的异常,如被0除、数组下标越界等,其产生比较频繁,处理麻烦,如果显式的声明或捕获将会对其程序可读性和运行效率影响很大。
因此由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理)

编译时异常和运行时异常的区别:
 * 编译期异常:Java程序必须显示处理,否则程序就会发生错误,无法通过编译
 * 运行期异常:无需显示处理,也可以和编译时异常一样处理

常用异常:
    
    ArithmeticException                    算术异常
    ArrayIndexOutOfBoundsException            下标越界
    ArrayStoreException                    数组存储异常
    ClassCastException                    类转换异常
    ClassNotFoundException                找不到类异常
    IllegalThreadStateException                非法线程异常
    InterruptedException                中断异常
    NoSuchFieldException                没有字段异常
    NoSuchMethodException                没有这个方法异常
    CloneNotSupportedException                不支持克隆
    NullPointerException                空对象异常
    NumberFormatException                数字格式化异常
    RuntimeException                    运行时异常
    UnsupportedOperationException            不支持当前操作异常

异常中要常用的几个方法:
 public String getMessage():异常的消息字符串        
 public String toString():返回异常的简单信息描述
          此对象的类的 name(全路径名)
          ": "(冒号和一个空格) 
          调用此对象 getLocalizedMessage()方法的结果 (默认返回的是getMessage()的内容)
 printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值 void 。把信息输出在控制台

处理异常的方式: 
try catch finally 三都不能独立存在, catch 和 finally 也不能组合使用,只能try 与 catch 或 try 与 finally 组合使用
        A:try...catch...finally
        B:try...catch
        C:try...catch...catch...
        D:try...catch...catch...finally
        E:try...finally

如果 catch 里面有 return 语句,请问 finally 里面的代码还会执行吗?如果会,请问是在 return 前,还是 return 后。
答: 会。前。准确的说,应该是在中间。

final,finally 和finalize的区别
 final :最终的意思,可以修饰类,成员变量,成员方法    修饰类,类不能被继承    修饰变量,变量是常量    修饰方法,方法不能被重写
 finally :是异常处理的一部分,用于释放资源。一般来说,代码肯定会执行,特殊情况:在执行到finally之前jvm退出了
 finalize :是 Object 类的一个方法,用于垃圾回收

异常处理第二种方式: 
格式: throws 异常类名
注意:这个格式必须跟在方法的括号后面。
throw:如果出现了异常情况,我们可以把该异常抛出,这个时候的抛出的应该是异常的对象

注意:
    尽量不要在main 方法上抛出异常。

throws 和 throw 的区别(面试题)
    throws
        用在方法声明后面,跟的是异常类名
        可以跟多个异常类名,用逗号隔开
        表示抛出异常,由该方法的调用者来处理
        throws 表示出现异常的一种可能性,并不一定会发生这些异常
    throw
        用在方法体内,跟的是异常对象名
        只能抛出一个异常对象名
        表示抛出异常,由方法体内的语句处理
        throw 则是抛出了异常,执行 throw 则一定抛出了某种异常
throw 语句可以单独使用, throw 语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。

自定义异常类 自定义异常类名以 Exception 结尾
两种方式: 
    A:继承 Exception-------编译期异常类
    B:继承 RuntimeException-----运行期异常类

------------------------------------ 线程 Thread ------------------------------------
进程: 就是正在运行的程序。进程是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
同一时间CPU只能执行一个进程,线程上下文切换比进程上下文切换要快得多。
线程是依赖于进程而存在的,一个进程中至少有一个线程。
线程是程序的执行单元,执行路径,是程序使用CPU的最基本单位。
单线程: 如果程序只有一条执行路径。
多线程: 如果程序有多条执行路径。
多线程的意义: 不是提高程序的执行速度,而是为了提高应用程序的使用率。

并发(Concurrent): 一个处理器同时处理多个任务。  逻辑上的同时发生
并发(Parallel): 多个处理器或是多核的处理器同时处理多个不同的任务。   物理上的同时发生。
Java程序的运行原理: 
    由java命令启动JVM,JVM启动就相当于启动了一个进程。
    接着由该进程创建了一个主线程去调用main方法。
JVM虚拟机的启动是单线程还是多线程的: 
    JVM虚拟机的启动时多线程的,原因是垃圾回收线程也要先启动,否则会很容易出现内存溢出。
    垃圾回收线程加上主线程,最低启动了两个线程,故,JVM的启动是多线程的。
线程的生命周期: 
    新建: 创建一个线程对象
    就绪: 调用start()方法后,线程有执行资格,但没有获取到执行权(也就是没有获取到CPU内存资源)
    运行: 获取到执行权(获取到CPU内存资源)
    阻塞: 没有执行资格也没有执行权,一般是调用方法 suspend() sleep() wait() join() 方法后线程进入阻塞状态
    死亡: run()方法结束,或线程被中断(调用方法stop()或destroy())

如何实现多线程:
    1.继承 Thread 类。
        A: 自定义类继承 Thread 类。
        B: 自定义类中重写run()方法。
        C: 创建自定义类对象
        D: 启动线程 使用start()方法
    2.实现 Runnable 接口。
        A: 自定义类实现 Runnable 接口。
        B: 自定义类中重写run()方法。
        C: 创建自定义类对象
        D: 创建 Thread 类对象,并把自定义类对象作为构造参数传递。

为什么要将 Runnable 接口的子类对象传递给 Thread 类的构造函数:
    因为自定义的run()方法所属的对象是 Runnable 接口的子类对象,
    所以要让线程去执行指定对象的run()方法就必须明确该run()方法所属对象。

两种实现多线程的方法的区别: 
    实现 Runnable 接口可以避免Java中单一继承的局限性;
    适合多个相同程序的代码去处理同一个资源的情况,把线程同程序代码数据有效的分离。
    增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
    继承 Thread : 线程代码存放在 Thread 子类run()方法中
    实现 Runnable : 线程代码存放在 Runnable 接口的子类的run()方法中。
Thread 类中的run()方法,是用于存储线程要运行的代码。
重写run()方法的目的是: 将自定义的代码存储在run()方法中,让线程运行。
一个线程只能调用一次start()方法,如果调用多次会出现: IllegalThreadStateException : 非法的线程状态异常

run()和start()的区别: 
    run() : 仅仅是封装被线程执行的代码,直接调用时普通方法
    start() : 首先启动了线程,然后再由JVM去调用该线程的run()方法


多线程安全问题的原因(判断一个程序是否有线程安全问题的依据)
    A : 是否有多线程环境
    B : 是否有共享数据
    C : 是否有多条语句操作共享数据

线程同步: 
    ①线程同步就是线程排队 
    ②只有共享资源的读写访问才需要同步
    ③只有变量才需要同步访问
    ④多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码。
解决线程安全问题: 
    A : 同步代码块:
        synchronized(对象){ 需要被同步的代码; }
        这里锁的对象是任意对象
    B : 同步方法
        把同步加在方法上。
        这里的锁对象是 this
    C : 静态同步方法
        把同步加在静态方法上
        这里的锁对象是 当前类的字节码文件对象 文件名.class
同一对象共享数据不需要加 static ,多个不同对象共享数据要加 static

多线程有几种实现方案,分别是哪几种?
    两种  继承 Thread 类,实现 Runnable 接口
    扩展一种: 实现 Callable 接口,和线程池结合。重写call()方法
同步有几种方式?分别是什么?
    同步代码块,同步方法,同步静态方法
sleep()和wait()方法的区别:
    sleep() : 必须值时间;不释放锁;
    wait() : 可以不指定时间,也可以指定时间;释放锁;
为什么wait(),notify(),notifyAll()等方法都定义在 Object 类中?
    因为这些方法都调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。
    而 Object 代表任意的对象,所以,定义在这里面。

当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
    分几种情况:
     1.其他方法前是否加了synchronized关键字,如果没加,则能。
     2.如果这个方法内部调用了wait,则可以进入其他synchronized方法。
     3.如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。
     4.如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。

简述synchronized和java.util.concurrent.locks.Lock的异同?
    主要相同点:Lock能完成synchronized所实现的所有功能
    主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。
            synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。

守护线程:也?

10-05 16:21