java总结第三周

java

一. 关键字,标识符,常量

1. 学习了计算机基础知识(软件/硬件)

2. java语言的特点:

    面向对象语言的一种

    跨平台

    开源

3. 了解了java平台版本以及历史.

4. 开发一个Java应用程序

    jre:Java程序运行环境,也包括jvm以及java所需的核心类库.

    jvm:java虚拟机

    jdk:java开发工具包,包含jre以及开发工具的包.
                  三者之间的关系: jvm<jre<jdk

5. 了解了什么是关键字:

关键字是指被Java语言赋予特定含义的词.

​       class :表示创建一个类(Java语言最基本的单元)

​       public:权限修饰符的一种

​       static:静态修饰符号

​       void:和java的方法有关

6. 什么是标识符:

 针对包,类,接口,方法,变量,常量等起名字的字符序列

标识符的组成:
​      26个英文字母(大小写)

​      数字字符

​      $符号

​      _下划线符号

​      .严格区分大小写的

标识符的注意事项:
        1.数字字符不能作为标识符的开头位置
        2.不能是Java中的关键字
        3.Java严格区分大小写,我们在起名字的时候:见名知意
        4.满足条件:满足组成的一种即可!不能是非法字符

7. 包,类,接口,方法,变量,常量,书写规则

: (目录/文件夹) 都是字母小写  或者可以有"_":  如果是一个单词   首字母大写 其余小写                         例:     class Student{}

           如果是多个单词组成    每个单词首字母都大写 其余小写          例:     class DataTypeDemo{}

    接口:      与 类 相同

    方法: 如果是一个单词  字母全部小写                             例: main ()

                                                                 int price = 10;

            如果是多个单词   第一个单词全部小写 从第二个单词开始每个单词的首字母大写其余小写

                                  例: checkUserName

                                    int appleprice = 10;

    变量:        与 方法 相同

    常量:   如果是单个单词  字母全部大写                                例:HELLO

             如果是多个单词  每个单词字母都大写  单词与单词之间用"_"隔开

8. 什么是常量:

在程序执行过,其值不发生改变的量     分为: 字面值常量  自定义常量

1.字面值常量:

​       字符串常量:使用双引号包裹起来的内容:称为"字符串常量"      "HelloWorld"

​       字符常量:    使用单引号包裹起来的内容      'a'        'A'

​       整数常量:    100     56 

​       小数常量:    2.15    0.64

​       布尔常量:    true    false 

​       空常量:      null

2.自定义常量(面向对象  关键字 final) 

​       进制也属于常量

二. 变量,运算符

1. 了解了更简单的方法: 8421码 可以快速的进行二进制到十进制,十进制到二进制的转换.

2. 有符号位的数据表示法

计算机底层对数据的计算: 是通过补码进行运算的

​    正数的 原码 反码 补码 相同

​    负数的最高符号位为1  

​    负数的反码是在原码的基础上,最高符号位不变,数值位按位取反.

​    负数的补码是在反码的基础上,最高符号位不变,数值位末尾+1.

3. 变量 在程序执行过程中,其值发生改变的量

变量的三要素

数据类型   

变量名: 满足标识符规则  

初始化值: 满足的范围即可

      格式:   数据类型  变量名 = 初始化值

4. 数据类型 :在Java中,数据类型分为两大类型:

A)基本数据类型:四类八种   (研究的都是基本类型)
    整数类型:默认int
           字节类型             byte          占1个字节(8个比特位) 取值范围:-128~127
           短整型              short          占2个字节
           整数默认类型         int            整数默认类型 占4个字节
           长整型              long           占8个字节
                                注意事项:必须在long的初始化值的末尾加L或者l
                                    
    浮点类型:默认类型double
           单精度         float      单精度      占4个字节
                                注意事项:
                                        float f = 12.56F ; //后面加上F或者f
           双进度         double     双进度       占8个字节
                                
    字符类型      char                           占2个字节 
                 初始化值:单引号括起来的单个内容
                                
    布尔类型      boolean     不会参与类型转换:仅仅表示真,假          占1个字节
                             要么是true/要么false
                                int a = 10 ;
                                int b  =20 ;
                                比较a与b是否相等:获取到boolean类型的结果
B)引用数据类型:后面说
             数组,类,接口
 

5. 定义变量的注意事项:

在java语言(强类型语言:语法结构很严谨)中,同一个变量不能重复定义
                                 (javascript语言:弱类型语言:可以去重复定义变量)

​      一行就写一个变量即可!  一行结束之后分号;(代码规范风格)

​      一行也可以定义多个变量;

​      变量要么直接初始化,要么先定义,但是必须在使用之前对其进行初始化

变量要进行运算,必须要确定数据类型的一致

​      在Java中有一个隐式类型转换: byte,short,char三者之间不进行相互转换,

​      参与运算, 优先转换为int类型,long,float-double类型

6. 显示转换与隐式转换

显示转换(强制转换) :      大的数据类型----->小的数据类型
             格式:      目标数据类型 变量名  = (目标数据类型)初始化值;   会有损精度!

隐式转换:(隐式类型提升)    byte,char,short三者之间不转换,一旦参与运算,优先提升为int...

​       超出了 byte类型 的范围为-128~127   

​       先算出int 类型的二进制(4字节)  然后化为byte(1字节) 类型的二进制

​       最高符号为0,原码 ,反码,补码都相同

​       最高符号为1 数值位-1 求反码  数值位按位取反求原码

7. Java中的运算符号:

(1) 算术运算符            +      -     *      /        %
    扩展算术运算符        ++     --

第一种:        
    ++或者--单独使用

​    无论++或者--在数据前面还是数据后:都是对当前数据本身自增1或者自减1
    
第二种:
​    ++或者--参与运算使用
    
​    如果++或者--在数据的前面:需要先进行自增1或者自减1,然后再参与运算!

​    如果++或者--在数据的后面:先进行运算,然后再进行自增1或者自减1

(2) 赋值运算符             =

    扩展的赋值运算符:将符号右边的数据和左边的数据相加,然后再赋值给左边的这个变量
                    +=,-=,*=,/=,%=

   +=举例:
       int a = 10 ;
       a += 20 ;
       类似于 a = a + 20 ;

   举例:

short s = 1;

1)s = s + 1 ;
2)s+=1 ;

以上代码1),2)哪一句会编译失败?为什么?哪一句编译成功?
1)编译失败
byte,short,char三者不转换,一旦参与运算,先提升为int类型,然后再参与运算!

2)编译成功:
        s+= 1  ; 类似于s = s + 1 ;   
        扩展的赋值运算符中:
            特点:隐藏了强转类型转换
            等价于 s = (short)(s+1) ;

8. 关系(比较)运算符

=,<,<=,>=,>,==

无论我们的表达式是简单还是复杂的,最终比较运算符的结果不是true,就是false

注意:        ==不能写成= 

​        数学表达式:
         3 <=x <=5    x>=3 && x<=5 Java语言(逻辑符号)    

9. 逻辑运算符

基本的逻辑运算符号:
​    逻辑单与:       &              并列关系(满足全部条件)         有false,则false
                
​    逻辑单或:       |              或的关系:满足一个条件即可       有true,则true
                 
​    逻辑异或:       ^              相同则为false,不同则为true

​    逻辑非:         !             非true,则false;非false则true         偶数个非是他本身

扩展的逻辑运算符:

​    逻辑双与:       &&             左边有false,右边则不执行

​    逻辑双或:       ||             左边有true,右边则不执行

10. 位运算符

基本的位运算符号: 

​     位与:              &             有0则0

​     位或:              |             有1则1
            
​     位异或:            ^             相同则为0 不同则为1   
                                      特点:  一个数据被另一个数据位异或两次,其值是他本身!

位运算之位移符号:

​    <<:    左移
         将数据的补码进行左移动,右边不够的补0;将最高符位丢弃掉
​    >>:    右移
         将数据的补码进行右移动;如果最高符号位为1,则左边补1;
         最高符号位为0,则左边补0;
​    >>>:    无符号右移
         无论最高符号位是1还是0,左边始终补0

​    左移的特点:           <<:将左边的数据乘以2的移动次幂
                
​    右移的特点:           >>:将左边的数据除以2的移动次幂

11. 三元(三目)运算符

 int temp = (x > y )? x: y ;
     int result = (temp > z)? temp : z ;

 或者

    int max2 = (x>y)?((x>z)?x:z):((y>z)?y:z) ;

三. 语句

1. 键盘录入

使用步骤

(1). 导包: 在java语言中:只要不是java.lang包下的类都需要导入!
位置:class上面  
 import java.util.Scanner;
    

(2). 固定格式: 创建键盘录入对象(文本扫描器对象)
Scanner 对象名 = new Scanner(System.in) ;
    
(3). 开始录入数据  :使用int类型举例
int 变量名 =  对象名.nextInt(); 
import java.util.Scanner;
class LiXin6{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个值");
int a = sc.nextInt();
System.out.println("请输入第二个值");
int b = sc.nextInt();
System.out.println("请输入第三个值");
int c = sc.nextInt();
int max2 = (a>b)?((a>c)?a:c):((b>c)?b:c);
System.out.println("最大值为:"+max2);		
}
}
      public String nextLine():正式用法:
import java.util.Scanner;
class ScannerDemo2{
public static void main(String[] args){
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
//提示并录入数据
System.out.println("请输入一个字符串数据:") ;
String line = sc.nextLine() ;
System.out.println("您输入的字符串是:"+line) ; //"helloworld"
    }
}

如果同时录入两个int类型,
同时录入两个String类型
或者 先录入String,在录入int
先录入int,在录入String:哪一个会有问题?   

(4). 键盘录入的细节:
           如果先录入int,再录入String会出现,会漏掉第二个数据String
           原因就是 输入回车符号-->才能录入数据 ;回车符号---> (换行)

方案1:在录入字符串之前,新创建一个键盘录入对象

    //先录入int,在录入String字符串
    Scanner sc = new Scanner(System.in) ;
	System.out.println("请输入第一个数据:") ;
	int a = sc.nextInt() ;
	
	//创建一个新的键盘录入
	Scanner sc2 = new Scanner(System.in) ;
	System.out.println("请输入第二个数据:") ;
	String b = sc2.nextLine() ;
方案2:Scanner类提供了这个功能:
		public String next():录入一个字符串 (非正式用法) 
        System.out.println("请输入第二个数据:") ;
        String b = sc.next() ;     

2. if 语句

格式一:
        if(表达式){
            语句;
        }

格式二:

if(表达式){
    语句1;
}else{
    语句2;
}

   if 语句的嵌套: 

if(表达式1){
    if(表达式2){
        语句1;
    }else{
        语句2;
    }
}else{
    if(表达式3){
        语句3;
    }else{
        语句4;
    }
}

if 语句格式三 :

if(表达式1){
    语句1;
}else if(表达式2){
    语句2;
...
...
...
}else{
    语句n;
}

3. switch语句

switch语句格式:
    
switch(表达式){
case 值1: 
语句1;
break ;
case 值2:
语句2;
break ;
 ...
 ...
    default:
            语句n;
            break ;
    }

switch语句使用的注意事项:

(1). case语句后面的值只能是常量,不能是变量(Java是一个强类型语言的:语法结构非常严谨) 

(2). 书写switch语句的时候,case语句必须存在break语句,结束switch语句的! 如果没有书写break语句,会造成"case穿透!"现象

(3). switch语句的结束条件

​       a)遇见break结束

​       b)程序默认执行末尾结束

4. for 语句 (明确循环次数使用)

for的格式:
   for(初始化语句;条件表达式;控制体语句;){
    循环体语句;
   }
   
for 语句的嵌套:
       for(初始化语句;条件表达式;控制提语句){
           for(初始化语句;条件表达式;控制体语句){             
           }
       }

5. while 语句 (不明确循环次数使用)

while语句的格式:
     初始化语句;
     while(条件表达式);
     循环体语句;
     控制体语句;
使用的场景不同

6. 死循环 break可跳出死循环

死循环的格式有两种:          
1. for(;;){             
循环体语句;                    
}          
2. while(true){            
循环体语句;                    
}

7. do while 循环语句

do while循环语句的格式:
     do{
         循环体语句;
         控制体语句;
       }
       while(条件表达式);

8. for while 和 do while的区别

1.格式的不同
2.for循环节省内存空间:
       for循环结束,变量随着被释放掉,节省内存空间;(不能访问这个变量了.)
       while循环结束,依然可以访问这个变量,比较消耗内存空间...
3.dowhile:优先循环体语句,即使条件不成立,循环体至少执行一次
    开发中:
         优先for,其次while,再次do-while

9. 跳转控制语句有三个关键字 :

break: 结束中断,结束循环(不能单独使用)switch和循环中使用
continue: 继续, 在循环中使用 ,结束当前循环,立即进入下一次循环
return:很少单独使用,结合有具体返回值类型的方法使用!     结束方法的;

看程序:写结果

   for(int i = 1; i <= 10; i++) { //i=1,i=2,i=3,i=4,i=5,i=6....i=9
          if (i % 3 == 0) {
              //在此处填写代码      
          }
          System.out.println("java基础班");

		  //1)2),3),4)
      }
      在控制台输出2次“java基础班”         break;结束中断
      在控制台输出7次“java基础班”         continue:结束当前循环,立即进入下一次循环
      在控制台输出13次“java基础班”        System.out.println("java基础班"); 

四. 方法

1. 方法:就是使用{}代码块包起来,并且起一个名字(见名知意)

Java中定义方法的格式:
(1).有具体返回值类型的方法的定义
	固定格式
		 public static 返回值类型 方法名(小驼峰命名法)(参数类型1 变量名1,参数类型2 变量名2....){
				                                     int a,int b,intc 
			    ...
				return 结果;
			 
		 }

		 定义两个数据之和的功能时候,
		两个明确
			1)明确返回值类型:int
			2)明确参数类型以及参数个数
				int类型 2个参数
public static int sum(int a,int b){//形式参数
		int result = a + b;//30+20
		return result ;
            }
}: class Text {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入第一个值");
        int a = sc.nextInt();
        System.out.println("请输入第二个值");
        int b = sc.nextInt();
        System.out.println("请输入第三个值");
        int c = sc.nextInt();
        int max = sum(a, b, c);
        System.out.println(max);
    }

    public static int sum(int a, int b, int c) {
        int max = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
        return max;
    }
}

2. 方法使用中的注意事项

(1). 方法和方法是平级关系,不能进行嵌套:    在一个方法中定义另一个方法不允许
			
(2). 在Java中,定义方法的时候形式参数必须携带数据类型! (Java是一个强类型语言)					
	 
(3). 调用方法的时候,传递的实际参数不需要在携带数据类型了 
        
(4). 定义方法的时候: 有{括号的地方不能有分号;     

没有具体返回值的类型的方法的定义:
    没有具体返回值类型的方法调用
    1)单独调用: 只能单独调用
    2)输出调用:
    3)赋值调用:
                 
    //void v = printStar() ; //FunctionDemo.java:28: 错误: 非法的表达式开始
    //输出调用
    //System.out.println(printStar()) ; 不行

    //单独调用
    //printStar(m,n) ;

3. 方法重载 :

方法重载(overload) :方法名相同,参数列表不同,与返回值无关!
	参数列表不同:
	 1)参数个数不同
	 2)参数类型不同

五. 数组

1. 数组 : 就是存储多个数据的容器,必须保证容器内数据类型的一致.

2.数组的定义格式:

  动态初始化 :
            给定了数组的长度,系统默认对元素进行初始化!
            数据类型 数组名称[] = new 数据类型[数组长度];
            数组类型 []数组名称 = new 数据类型[数组长度];
                 例:         int[] arr ;定义一个int类型的数组arr
		                     int[] arr = new int[3] ;
		                     int arr[];定义了一个int类型的arr数组
		                     int arr[] = new int[3] ;
             length : 获取数组长度.
  静态初始化 :
            我们给定的具体的元素,数组长度由系统确定
            数据类型 数组名称[] = new 数据类型{元素 1, 元素 2, ....};
            数据类型  []数组名称 = new 数据类型{元素 1, 元素 2,....};
            可简写为 : 数据类型 数组名称[] = {元素 1, 元素 2,.... };
                      数据类型 []数组名称 = {元素 1, 元素 2,....};             

3. 遍历

printArray2(arr) ; //调用方法
public static void printArray2(int[] arr){
System.out.print("[") ;
//遍历数组
for(int x = 0 ; x < arr.length ; x ++){
	//判断:如果当前x :取到最大索引值 arr.length-1
	if(x==arr.length-1){
		System.out.println(arr[x]+"]") ;
	}else{
		//不是最后一个索引值,中间的元素 arr[x]+", " 不换行
		System.out.print(arr[x]+", ") ;
	}
}
}
//  栈:存储都是局部变量(在方法定义中或者方法声明上)
//  堆:new出来的东西,创建对象 (里面存储:"成员变量")
//  方法区:有哪些方法--分别存储在哪个类中---xxx.class()

4. 数组的基本应用 :

求最值问题:
    class Text {
    public static void main(String[]args) {
        int[] arr = {69,13,56,87,24} ;
        //调用方法
        int result = getMax(arr);
        System.out.println("数组中的最大值是:" + result);
    }
        public static int getMax(int[] arr){
            //假设思想:
            int max = arr[0] ;
            for(int x = 1 ; x < arr.length ; x ++){
                //判断
                if(arr[x] > max){
                    max = arr[x] ;
                }
            }
            return max ;
        }
    }

5. 冒泡排序:

 //冒泡:两两比较,较大的值往后放,第一次比较完毕,最大值出现在最大索引处;总共比较的次数:数组长度-1次
class Text {
public static void main(String[] args){
    int arr[] = {12,24,6,35,21};
    for (int x = 0;x < arr.length-1;x++){
        for (int y = 0;y < arr.length-1-x;y++){
            if (arr[y] > arr[y+1]){
                int a = arr[y];
                arr[y] = arr[y+1];
                arr[y+1] = a;
            }
        }
    }
    sum (arr);
}
public static void sum(int arr[]){
    System.out.print("[");
    for (int x = 0; x < arr.length;x++){
        if (x == arr.length-1){
            System.out.print(arr[x]+"]");
        }else{
            System.out.print(arr[x]+",");
        }
    }
}
}

6. 方法的形式参数问题 :

​ 形式参数的改变不能影响实际参数

7. 类与对象的关系 :

 类 : 能够描述现实世界真实事物的一组属性和行为的集合!
 事物 : 现实真是存在的事物;
                 
 创建对象 : 格式:类名 对象名 = new 类名();	
            
 面向对象 : 
         面向对象的思想特点:
	        1)更符号我们生活中是思想行为习惯
			2)让复杂的事情简单化
			3)我们从执行者变成了指挥者   
 三大特点 : 封装,继承,多态

8. 成员变量和局部变量的区别 :

1)在程序中的书写位置不同
	局部变量:
		   方法定义中或者方法声明上
	成员变量:
		   在类中,成员方法外定义的变量	
2)在内存中	
	局部变量:
			在栈内存中
	成员变量:
			在堆内存中		
3)生命周期不同
	局部变量:
			随着方法调用而存在,随着方法调用完毕而消失!
	成员变量:
			随着对象的创建而存在,随着对象创建完毕之后,不会立即消失,
							需要等待GC(垃圾回收器空闲时候回收掉!)
									GC算法----->标记算法
												标记---清除												
4)初始化不同
	局部变量:
			可以先定义,但是必须在使用之前必须赋值,否则:可能尚未初始化变量			
	成员变量:
			可以不初始化,它存在系统默认初始化!(根据类型判定)
                
                
    如果一个方法的形式参数是引用类型  是具体类,那么调用该方法时,实际参数如何传递  
     
     
 //学生类
class Student{
	//有一个成员方法:学习的方法
	public void study(){
		System.out.println("Good Good Study ,Day Day Up!!") ;
	}
}
//定义一个StudentDemo类
class StudentDemo{
	//有一个method成员方法
	public void method(Student s){ //方法的形式参数是Student类型
										//调用method 方法的时候:实际参数需要传递的是当前类的具体对象
										//Student s = new Student();
		s.study() ;					//对象名.方法() ;		//stu.study() ;
	}
}
//测试类
class StudentTest{
    public static void main(String[] args){
	//在测试类中访问StudentDemo类中的method方法?
	//创建StudentDemo类对象
	StudentDemo sd = new StudentDemo() ;
	//sd.method(s) ;//找不到符号
	
	//先去创建学生对象
	Student stu = new Student() ;
	sd.method(stu) ;
    }
}

9. 匿名对象

没有名字的对象
        格式:
		    new 类名() ;

匿名对象有一个特点:可以作为参数进行传递
			        在开发中,匿名对象使用一次即可! 
			        (因为没有栈内存变量指向堆内存地址,直接是在堆内存开辟空间,使用完毕,立即被回收!)
    class NoNameObjectDemo{
    public static void main(String[] args){
	//之前的写法
	//访问StudentDemo类中的method 方法
	//创建StudentDemo类对象
	StudentDemo sd = new StudentDemo() ;
	
	//创建一个具体的学生对象
	Student s = new Student() ;
	sd.method(s) ;
	
	System.out.println("------------------------------") ;
	//方式2
	StudentDemo sd2 = new StudentDemo() ;
	sd2.method(new Student()) ;
	System.out.println("------------------------------") ;
	
	//链式编程
	//一步走
	new StudentDemo().method(new Student()) ;
}
}

10. 封装

​ 目的 : 保证数据的安全性

11. private关键字的特点

​ 1)可以修饰成员变量,也可以修饰成员方法,但是都只能在本类访问,外界类不能够访问
​ 2)这些被私有修饰的成员变量,或者成员方法,可以间接通过公共方法来访问!

12. this

 this:解决局部变量隐藏了成员变量
 格式 : this.成员变量名  = 局部变量; 
 this:就是代表当前类的对象的地址值引用!
        this.变量名 :访问的本类的成员变量
        this.方法名():访问的是本类的成员方法
        this() ; 访问本类无参构造方法
        this(String xx):访问本类的有参构造方法            

13. 构造方法

 1)构造方法名和类名一致
 2)没有具体的返回值类型
 3)连void都没有
 构造方法的目的:为了给类的成员的一些数据进行初始化	
                 
        无参构造方法 :
        
                 private String brand ;
	             private int price ;//价格
	             private String color ;
                 public Phone(){
	             System.out.println("这是phone类的无参构造方法...") ;
                 }
                 ....
                 ....
                 Phone p = new Phone();//
		         //setXXX()赋值
		         p.setBrand("锤子手机") ;
		         p.setPrice(1299) ;
		         p.setColor("黑色") ;
		         System.out.println("品牌:"+p.getBrand()+",价格:"+p.getPrice()+",颜色:"+p.getColor()) ;
		         System.out.println(p) ;
		         
        有参构造方法 :
        
                 public Student(String name){
	             //this.name = name ;
	             System.out.println("这是Student类带String类型的有参构造方法...") ;
                 }
                 ....     (set xxx,get xxx)
                 ....
                 Phone p2 = new Phone("锤子手机",1299,"黑色") ; 
		         System.out.println("品牌:"+p2.getBrand()+",价格:"+p2.getPrice()+",颜色:"+p2.getColor()) ;
                }
一个类的成员:
			1)成员变量
			2)成员方法
			3)构造方法

14. 静态static关键字的特点:

  1)随着类的加载而加载
  2)优先于对象存在:  它不能this共存   (this:代表当期类对象的地址值引用)
               对象还没有new的时候,当前被static修饰的成员就已经内存了

  3)被静态修饰的 可以被多个对象共享:有共享共用的意思

  4)被静态修饰的变量,方法----->静态变量或者静态方法
           我们所说的成员变量和成员方法:都指的是非静态
           静态的成员的访问方式:类名.变量
                                类名.方法名()
关于static关键字的使用注意事项:

   1)非静态的方法既可以访问静态变量,也可以访问非静态的变量
                既可以调用静态方法,也可以调用非静态方法

   2)静态的方法:只能访问静态变量,
               只能调用静态方法
               简单记:静态只能访问静态

15. 代码块

 在java中用{}包起来的内容,称为代码块.
 分类 :
      局部代码块 : 在方法定义中使用,作用:限定局部变量的生命周期.
      构造代码块 : 在类的成员位置(类中,方法外),使用使用{}包裹起来 
                  作用:给类中的一些成员进行数据初始化                
                  特点:每次在执行构造方法之前,如果存在构造代码块,先执行构造代码块中的内容!
      静态代码块 : 在类的成员位置,直接使用 static{}.    静态代码块就加载一次!
                  作用:也可以通过static代码块,对一些操作(后期IO流创建文件/JDBC)
                  特点:随着类的加载而加载,优先于对象存在!
      //     优先级:
      //     静态代码块(只执行一次) > 构造代码块 > 构造方法

16. 继承

 将多个类的共性内容抽取到一个独立的类中,然后这多个类和独立的这个类产生一种关系 : 继承关系
      关键字 : extends
      书写格式 :class Fu{}
               class Zi extends Fu{}
      继承的好处:
              1)提高代码的维护性
              2)提高代码的复用性
              3)让类和类之间产生的关系,是"多态的前提条件"
      继承的特点 :
              1)类与类之间的关系,继承关系,只支持单继承
              2)不支持多继承,但是可以支持多层继承
      继承中使用的注意事项 :
              1)子类继承父类 :可以继承父类的非私有的成员,私有的成员外界不能访问的,只能在本类中访问,                             但是可以通过公共方法间接访问.
              2)构造方法是不能被继承的,但是子类可以间接通过 super 关键字访问父类的构造方法.
一个类的组成 :
           成员变量
           构造方法
           成员方法

 继承中,每一个成员变量的关系问题

 成员变量 :
         a)子类继承父类,如果子类中的成员变量名称和父类的成员变量名称不一致,分别访问即可!
         b)子类继承父类,如果子类的成员变量名称和父类的成员变量名称一致:如何访问呢? (重点)
            1)首先在子类的局部位置找,是否存在局部变量名称,如果有,就使用
            2)如果没有,就在子类的成员位置找,是否存在这个变量,如果存在,就使用
            3)如果在子类的成员位置中没有找到,直接在父类的成员位置中找,如果有,就是使用!
            4)如果父类的成员位置都没有,就没有这个变量,报错!
                  // 遵循一个原则:就近原则!
继承中构造方法的访问 :

         a)子类继承父类,子类的所有的构造方法都会默认的访问父类的无参方法.
           子类的所有构造方法的第一句话:默认隐藏了super() ;  
            因为子类中肯能会使用到父类的数据,所以在继承关系中,
            得先让父类初始化---->构造方法  : 分层初始化!(先父类无参构造方法,在执行子类的构造方法)

         super:代表的父类对象的空间表示(父类对象的地址值引用!)
         b)如果父类中的无参构造方法没有,子类会怎么样?
           子类的所有的构造都会报错! (因为子类所有构造方法默认父类的无参构造方法!)
         如何解决呢?
              方式1:手动给出父类的无参构造方法(推荐)
              方式2:在子类的构造方法中的第一句话:通过super(xxx),间接的访问父类的有参构造方法
              方式3:只要子类的所有构造方法中一个能够让父类初始化即可!
               在子类的有参构造方法中:this():---->访问本类的无参构造方法,然后再子类的无参构造方法中
               间接访问父类的有参构造方法super(xxx) ;

   如何正确使用继承关系(extends)
   如果一个A类是B类的一种,或者B类是A类的一种,这个时候就可以使用继承关系
           继承关系:现实世界事物中本质体现的是一种 "is a"的关系

17. 总结:this和super 的区别

 this:代表的当前类对象的地址值引用
       super:代表的父类对象的地址值引用(代表父类的空间标识)

 访问成员变量
       this.变量名; 访问的本类中的成员变量
       super.变量名; 访问的是父类的成员变量

 访问构造方法:
       this() ; 访问本类的无参构造方法
       super() ;访问的父类的无参构造方法

       this(xxx);访问的本类的有参构造方法
       super(xxx);访问的父类的有参构造方法
 成员方法:
       this.方法名();访问的是本类的成员方法
       super.方法名() ;访问的是父类的成员方法

18.方法重载,方法重写

//如果子类出现了和父类一模一样的方法声明,叫做方法重写!(Override) —方法复写(覆盖)
//方法重写和方法重载的区别?

  方法重载:Overload   
  在一个类中,提供n多个功能,这些功能,方法名相同,参数列表不同,与返回值无关(目的:提高某个功能的扩展性)
           参数列表不同:
                      1)类型不同
                      2)个数不同
                      3)考虑参数类型的顺序
                        public static void open(int a,double d){}
                        public static void open(double a,int b){}
                   构造方法也可以重载!
   重载的目的:为了提高功能的扩展性:同一个方法可以接收很多类型的参数

  方法重写:Override
  在继承关系中,子类出现了父类一模一样的方法声明,重写的目的:子类有自己的功能,需要将父类的该功能覆盖掉!
  重写的目的:为了沿用父类的功能,并且还需要使用子类的功能(具体的子类才具备具体的动作...)
                           举例:
                                 动物:
                                        都具备吃和睡的功能
                                         猫和狗都继承自动物类,他们吃的不一样的
                                         猫和狗就需要讲吃和睡的功能覆盖掉...
                                               猫具体吃鱼
                                               狗具体吃肉

19.关键子 : final

final(状态修饰符):最终的,无法更改的
    关于final关键字的特点:
       1)可以修饰类,该类不能被继承!
       2)可以修饰符成员方法,成员方法不能重写!  (根据具体的题意要求!)
       3)可以修饰的变量,这个变量此时是一个常量! (自定义常量)

final修饰基本数据类型和引用类型的区别?
          final修饰基本数据类型: 基本数据类型的对应的数据值不能在被赋值了,只能赋值一次!
          final修饰引用类型:引用数据类型对应的地址值不能被改变
               final Student s = new Student() ;//修饰实例变量s,s的堆内存地址值永远是固定值!
                             s = new Student() ;//重新开辟空间(报错)

六.多态

1.多态 : 一个事物在不同时刻不同形态.

 多态的前提条件:
           1)必须存在继承关系 (extends)
           2)必须存在方法重写
                   子类需要覆盖父类的功能
                Animal
                    eat():"动物都需要吃饭..."
                Cat
                    eat() "猫吃鱼"
                Dog
                    eat() "狗吃骨头"

          3)必须有父类引用指向子类对象
               class Fu{}
               class Zi extends Fu{
                   //存在重写
               }

               格式: Fu fu = new Zi() ;

2. 多态成员的访问特点

 Fu f = new Zi() ;
     成员变量:编译看左(看Fu类是否存在变量,存在,编译不会报错!)
             运行看左(使用Fu类的东西)

     成员方法:(一般没有明确是静态方法的都是----->非静态)
            编译看左(看Fu类是否存在这个方法,存在,编译不会报错!)
            运行看右(存在方法重写,所以最终子类的功能将父类的该功能进行覆盖!)

     静态的方法:
             编译看左看Fu类是否存在这个静态方法,存在,编译不会报错!),
             运行看左(静态方法:子类出现了父类一模一样的静态方法,算不上重写,跟类相关的-->类成员)                       
             静态功能推荐的方式:类名.访问


     构造方法:
             即使多态的情况进行测试,多态的前提条件---->继承关系
             当前执行子类的构造方法之前,需要让父类的先进行初始化,然后子类进行初始化(分层初始化!)

3. 多态的好处

1)提高代码的复用性:由继承保证

2)提高了代码的扩展性:由多态保证 (重点)
            Fu fu = new Zi() ; 父类引用可以指向子类对象     

4. 多态的弊端:

不能访问子类的特有功能   (Fu f = new Zi())
                       f.方法名() ;报错了. 父类中没有子类特有功能!
如何解决多态的弊端?

      方案1: (不推荐)
             具体的子类创建具体的子类对象  Zi z = new Zi() ;
             z.成员方法名() ;

             本身Fu f = new Zi() ;已经在堆内存中开辟空间了
              Zi z = new Zi() ;在堆内存中又开辟空间,从内存角度考虑,这种比较消耗内存空间,不太好!

      方案2:(推荐使用:"向下转型")
            多态的第三个前提条件:父类 引用指向子类对象      :"向上转型 "Fu f = new Zi() ;
            能不能将父类的引用转换成子类的引用?   好处:不需要在堆内存开辟空间
            可以------>"向下转型"
                Zi z =   (Zi)f ;  还原成子类型

           强转类型转换: 目标类型 变量名 =(目标类型)初始化值;

                  基本类型 :   int num =  65 ;
                     //num--->char类型
                  char ch  = (char)num ; ====>'A'

5. 多态的向上转型和向下转型

多态的向上转型:多态的第三个前提条件--->父类引用指向子类对象	
		格式:Fu fu = new Zi() ;
		
    多态的弊端:不能访问子类的特有功能
    可以使用向下转型:
         将父类的强转强转转换为子类引用
          Zi zi = (Zi)fu;

多态的方式:使用向下转型时,可能出现异常?
           要使用向下转型,前提必须有父类引用指向子类对象 Fu f = new Zi() ;

           遵循向下转型的格式:
                       Zi z = (Zi)f;     必须心里清楚堆内存存储的类型....

  向下转型使用不当,就出现java.lang.ClassCastException:类转换异常: (属于运行时期异常)
               当前堆内存中的实例不是该类型时,就会出现问题!             

6. 抽象类

什么是抽象类----->现实世界事物中,某个事物是比较概括性(人/水果/动物),描述为抽象事物,
  只有具体的工人/苹果/猫或者狗,才具备具体的功能;
  将某个事物中的一些功能仅仅给出声明即可,没有方法体----->抽象方法---->此时这个类必须为抽象类!

           举例:
                   动物都需要吃和睡
                   只要看到具体的动物类:猫类/狗类,才具备吃和睡的功能
                   将动物类中的吃和睡给出一个声明:加入一个关键字 abstract:抽象方法
                   动物类----->抽象类


     抽象的关键字:Java语言 :abstract关键字 (抽象的含义)

     抽象方法的格式;
       权限修饰符(一般情况都是public) abstract 返回值类型  方法名(形式参数列表) ;

     抽象类的格式:
     abstract  class 类名{}

7. 抽象类的特点

1)有抽象方法的类一定是抽象类
2)抽象类中不一定只有抽象方法 ,还可以非抽象方法(有方法体)
3)抽象类不能实例化---->意思:不能创建对象
               如何实例化呢:通过具体的子类进行实例化(进行对象的创建), 抽象类多态  Fu fu = new Zi() ;Fu类型 抽象类型
4)抽象类的子类有两种情况:
      a)目前来说:如果抽象类的子类都是抽象类---毫无意义  因为子类也不能new ,除非再有具体的子类
      b)抽象类的子类具体类---才能new     :抽象多态的形式  Fu fu = new Zi() ;

抽象类的核心宗旨:就是强制子类必须完成的事情(要将父类中的所有的抽象方法必须重写,否则报错!)
     
抽象类成员特点:
       成员变量
           既可以定义变量,也可以常量:被final修饰
       成员方法
           既可以定义为抽象方法,也可以定义为非抽象方法
           如果定义为抽象方法:关键字abstract(显示给出)
       构造方法
           存在无参构造/有参构造方法---->目的:分层初始化
               
一个类中没有抽象方法,那么将这个类定义为抽象类的意义何在?
       意义:为了不让它直接实例化!
       如何实例化:
               情况1)直接就有具体的子类
               情况2)间接的有具体的子类                 

8.接口

接口---->它的本质就是体现一个现实世界事物所具有的的额外的扩展功能!
         定义格式:    interface  接口名{}  ------>接口名遵循"标识符规则"---->大驼峰命名法

接口中的方法:不能有方法体,隐藏public abstract关键字,只能是抽象方法,不能有方法体

9. 接口的特点

   1)不能实例化(不能创建对象)
   2)如何实例化
           接口实例化: 通过接口的子实现类(一定是具体类)进行实例化----接口多态(使用时最多的!)

           子实现类和接口的关系: implements 实现关系


           接口的子实现类如果是抽象类----->肯定要存在抽象类的具体的子类,否则都不能实例化!

接口的子实现类的命名规则:
       开发中--->在接口名的后面+Impl:子实现类
                       interface Inter{}

                       class InterImpl  implements Inter{

                       }
接口的成员特点:
       1)接口中的成员方法:只能是抽象方法,默认的修饰符:public abstract(可以省略不写)
       2)接口没有构造方法
       3)接口的成员变量只能是常量:
                       存在默认修饰符:public static final (可以省略不写)
                           
类与类之间的关系:  extends 继承关系
                       只支持单继承,不支持多继承,但是可以多层继承

类和接口的关系:   implements关系:实现关系
                       一个类继承另一个类的同时,可以实现多个接口,中间逗号给隔开

接口与接口之间:   extends:继承关系
                       不仅支持单继承,也可以多继承,中间使用逗号隔开
    

10.形式参数问题的研究:引用类型


如果方法的形式参数是类 ,调用方法的时候,如何传递实际参数?
          具体类,调用该方法,实际参数需要传递当前具体类的对象
          抽象类,调用该方法实际参数需要传递的抽象类的子类对象 (抽象类多态)
          接口  调用该方式,实际参数需要传递的是当前接口的子实现类对象(接口多态)
    
例题:
//方法的形式参数是类  调用方法的时候,如何传递实际参数?
class Student{
    public void  study(){
        System.out.println("学习JavaEE...");
    }
}
class StudentDemo{
    public void method(Student s){
        s.study() ;
    }
}
//测试类
public class StudentTest {
    public static void main(String[] args) {
        //需要访问StudentDemo中的method 方法?
        //创建StudentDemo类对象
        StudentDemo sd = new StudentDemo() ;
        //创建一个具体的学生对象
        Student student = new Student() ;
        sd.method(student);
        System.out.println("-----------------------");
        //或者匿名对象
        new StudentDemo().method(new Student());
    }
}

//方法的形式参数是一个抽象类

abstract  class Person{
    //定义一个抽象方法
    public abstract  void work() ;
}
//PersonDemo类
class PersonDemo{
    public void show(Person person){    //形式参数是一个抽象类的情况:  Person  p  =   new Programmer();
        person.work();      //p.work();
    }
}

//定义Person抽象类的子类
class Programmer extends  Person{

    @Override
    public void work() {
        System.out.println("程序员不断的去学习...");
    }
}

//测试类
public class PersonTest {
    public static void main(String[] args) {
        //需求:要访问PersonDemo类中的show方法?实际参数如何传递?
        //创建PersonDemo类对象
        PersonDemo pd = new PersonDemo() ;
       // Person p = new Person() ; //抽象类不能实例化!   需要借助子类来进行实例化(存在具体的子类)
        //抽象类多态
        Person p = new Programmer() ;
        pd.show(p);
        System.out.println("-----------------------------");

        //匿名对象
        new PersonDemo().show(new Programmer());
     }
}

//方法的形式参数是一个接口类
interface Love{
    void love() ;
}

//LoveDemo类
class LoveDemo {                        //Love love = new LovePerson() p
    public void function(Love l){//方法的形式参数是接口类型,必须传递接口的子实现类对象
        l.love();//love.love() ;
    }
}

//定义一个接口的子实现类
class LovePerson implements  Love{

    @Override
    public void love() {
        System.out.println("爱生活,爱Java,爱高圆圆...");
    }
}
//测试类
public class LoveTest {
    public static void main(String[] args) {
        //需要访问LoveDemo类中的function方法
        //创建LoveDemo类对象
        LoveDemo ld = new LoveDemo() ;
        //Love love  = new Love() ; //接口不能实例化
        //接口多态
        Love love = new LovePerson() ;
        ld.function(love) ;
        System.out.println("------------------------");

        //匿名对象
        new LoveDemo().function(new LovePerson());
    }
}

如果一个方法的返回值是引用类型,最终方法结束,如何返回?
          具体类  :方法返回的就是当前具体类对象!
          抽象类 :需要返回的是抽象类的子类对象
          接口 :需要返回的是该接口的子实现类对象
例题:
//方法的返回值是一个具体类
class Student{
    public void study(){
        System.out.println("Good Good Study,Day Day Up!");
    }
}

//StudentDemo类
class StudentDemo {
    public Student show() { //方法的返回值是引用类型---具体类 :需要返回的当前类的具体对象
      /*  Student student = new Student() ;
        //???
        return student ;*/
        //匿名对象
        return new Student();
    }
}
//测试类
public class StudentTest {
    public static void main(String[] args) {
        //调用StudentDemo类中的show方法?
        //创建StudentDemo类对象
        StudentDemo sd = new StudentDemo() ;
        Student s = sd.show(); //Student s = new Student();  sd.show()---->返回的具体的学生对象
        s.study();
        System.out.println("-----------------------");
        //匿名对象
        Student student = new StudentDemo().show();
        student.study();
    }
}

//方法的返回值是一个抽象类
abstract class Person{
    public  abstract void work() ;
}
//PersonDemo类
class PersonDemo{
    public Person method(){ //返回值类型是一个引用类型---抽象类的情况,需要返回的是抽象类的子类对象
        //return ?
       // Person p = new Person() ;//抽象类不能实例化

        //抽象类多态
       // Person p = new Worker() ;
        //return p ;

        //一步走
        //匿名对象
        return new  Worker() ;
    }
}
//定义一个子类 继承Person类
class Worker extends  Person{

    @Override
    public void work() {
        System.out.println("工人不断的去工作...");
    }
}
//测试类
public class PersonTest {
    public static void main(String[] args) {
        //需要调用PersonDemo类中的method 方法?
        PersonDemo pd = new PersonDemo() ;
        Person person = pd.method(); // method方法其完成了一件事件: Person person  = new Worker() ;
        person.work() ;
        System.out.println("---------------------------------");
        //匿名对象
        Person p = new PersonDemo().method();
        p.work() ;
    }
}

//方法的返回值是一个接口
interface  Mary{
    public abstract  void mary() ;
}
//MaryDemo类
class MaryDemo{
    public Mary function(){//返回值类型是接口类型,
        //return ?

        //  Mary mary = new Mary() ; //接口不能实例化
        //需要提供接口的子实现类对象
        //接口多态
       // Mary mary = new You() ;
       // return mary ;


        //一步走
        return new You() ;
    }
}
//定义一个类
class You implements  Mary{
    @Override
    public void mary() {
        System.out.println("结婚了,很开心...");
    }
}

//测试类
public class LoveTest {
    public static void main(String[] args) {
        //调用MaryDemo类中的function方法?
        MaryDemo maryDemo = new MaryDemo() ;
        Mary mary = maryDemo.function();
        mary.mary();

        System.out.println("----------------------");

        //匿名对象
        Mary m = new MaryDemo().function();
        m.mary();
    }
}

11.关键词 : package

 包的真实含义:
            以后要代码分层...
            将包进行划分

 开发中,写包名的时候(字母都是小写,多级包) :公司域名反写

           开发中:
                   先按照模块划分,在按照功能划分
                           用户模块
                                  注册功能
                                    登录功能
                                    用户激活功能(使用邮件激活技术,手机短信验证,微信二维码)
                                    用户退出
                          商品模块
                          订单模块
                          购物车模块
                          支付模块                   

12.权限修饰符

 默认修饰符
 私有修饰符:private
 受保护的 :protected
 公共的,公开的:public

   修饰的权限从小到:private,默认,protected,public

七. 内部类

1. 内部类

在一个类中可以定义另一个类:

     在类A 中定义了类B,将类B就称为类A的内部类,类A就是外部类!

成员内部类:
       在一个类的成员位置中定义了另一个类

       内部类可以访问外部类的成员,包括私有!
         
外部类如何直接访问内部类的成员方法?
      格式:
          外部类名.内部类名  对象名 = 外部类对象.内部类对象;

成员内部类的修饰符:
        在成员内部类上面---加入private修饰:为了数据的安全性,它的访问---就要外部类的公共访问间接访问...
         
如果当前成员内部类是静态的,  里面的方法无论是静态的还是非静态的,都只能访问外部类的静态成员,包括私有!

如何直接访问静态成员内部类的成员呢?
       将静态的成员内部类看成是外部类的静态成员访问

       直接访问方式
               外部类名.内部类名 对象名 = new 外部类名.内部类名() ;
               

2. 局部类部类

关于局部内部类,它的书写位置,在外部类的成员方法中的定义的类
 
      局部内部类可以访问外部类的成员变量包括私有!
      在外部类的局部位置,访问内部类的成员方法,创建当前局部内部类对象来访问!

3. 局部内部类访问局部变量的时候,此时局部变量应该注意什么?

(JDK7/JDK8),为什么要加入final关键字呢?

如何此时Java环境是Jdk7,局部内部类访问局部变量时,此时该变量必须显示加入final修饰

目前环境是JDK8环境,做了什么优化?

通过反编译查看

class Outer2$1Inner2{
 
final int val$num;  num已经加入了final修饰

final Outer2 this$0;

public void show(){

System.out.println(val$num);

}

Outer2$1Inner2(){

this.this$0 = this$0;

val$num = I.this;

super();

}

}


原因:

  局部变量的生命周期是随着方法调用而存在,随着方法调用结束而消失

  而当前外部类对象调用method 方法的时候,此时num进入栈内存,在局部位置创建了局部内部类对象

  而局部内部类对象调用它的成员方法访问该变量,方法method方法结束之后,内部类对象不会立即消失,

  它里面的成员方法在访问局部变量,局部变量必须变成常量,常驻内存,否则如果当前变量消失了,局部内部类的成员依然在访问

  就会出现冲突! 所以 jdk7 收到必须加入final修饰,jdk8通过jvm已经做了优化了,无需手动加入final修饰
 

4. 匿名内部类

没有名字的内部类   一般在我们局部位置使用!

格式:
    匿名内部类它是内部类的一种简化格式
    new 类名(可以是抽象类,也可以具体类)或者是接口名(){
        重写功能
     } ;
 
匿名内部类的本质:
             继承了该类或者是实现了该接口的子类对象

关于匿名内部类在开发中的使用:
//       方法的形式参数如果是一个抽象类,那么实际参数可以需要接口的子类对象
              1)将子类定义出来 继承自抽象类
              2)直接使用抽象类的匿名内部类
//       方法的形式参数如果是一个接口,实际需要传递的接口子实现类对象
              方式1:将接口的子实现类定义出来
              方式2;使用接口的匿名内部类
//看程序,写结果
   要求:需要在控制台分别打印30,20,10
 
 考点:
   外部类直接访问非静态的成员内部类的格式
   外部类的成员变量的方法方式,(在成员内部类的成员方法中)
   成员变量,局部变量名称都一样(就近原则)
   外部类和内部类没有继承关系
   class Outer{
    int num = 10 ;

    //成员内部类
    class Inner{
        int num = 20 ;
        public void show(){
            int num = 30;
            //补全代码
            System.out.println(num);
            System.out.println(this.num);//this限定 :this.变量名:当前类的成员变量
           // System.out.println(new Outer().num) ; //方式1:new Outer().num

            System.out.println(Outer.this.num) ; //方式2:外部类的this限定
        }
    }
}
public class OuterTest {
    public static void main(String[] args) {
            Outer.Inner oi = new Outer().new Inner() ;
            oi.show();
    }
}
//        方法的返回值是一个抽象类?
//              需要返回的是当前抽象类的子类对象
//        方法的返回值是接口类型,需要返回的当前接口的子实现类对象 

5. java.lang.Object:是类结构层次的根类(超类—>父类),所有的类都默认继承自Object子类(派生类)

使用JDK提供的API文档学习常用类中的常用功能

API:Application Programming Interface:应用程序接口开发文档

Object功能类的getClass()方法

public final Class getClass():表示正在运行的类  (就是字节码文件对象)

Class----->反射的时候去使用
 
Class类:

       功能:
            public String getName():       获取当前类的全限定名称(包名.类名)

Object类中的getClass()/finalize()/hashCode()以及以后常用类

功能中如果native关键字:本地方法,非java语言底层实现另外一个功能

public int hashCode():(了解)获取对象的一个哈希码值 (本质不是地址值,可以把它理解为地址值)----跟哈希表有关系(HashMap)
一般情况:不同的对象获取的哈希码值是不同的 ,(但是中文字符,可能内容不一样,但是哈希码值不同!)
底层是通过哈希表算出来的.

6. 获取一个类的字节码文件对象有几种方式? 三种

第一种:通过Object类的getClass()--->Class    :正在运行的java类: class  包名.类名
 
第二种:任意Java类型的class属性----获取当前类的字节码文件对象Class

第三种方式:Class里面forName("类的全限定名称(包名.类名)") ; (使用最多)

7. Object的public String toString()

返回对象的字符串表示形式。结果应该是一个简明扼要的表达,容易让人阅读。
建议所有子类覆盖此方法。

描述一个对象:是由很多属性(成员变量组成),应该看到的具体的属性描述
要么手动方式(不推荐)
也可以直接快捷键----重写toString即可

大部分的常用类或者后面的集合都会重写Object类的toString()

8. Object类的equals方法

public boolean equals(Object obj)
判断当前obj对象是否和当前对象想等
  
面试题:
equals和==的区别?
 
==:    连接的基本数据类型: 比较的是数据值否相同

==:    连接的是引用类型: 比较的是地址值是否相同
 
equals方法:如果使用Object默认的:底层用==,默认比较的还是两个对象的地址值是否相同,

          Student s1  = new Student("文章",35) ;
          Student s2  = new Student("文章",35) ;

          s1和s2虽然地址值不同,他们的成员的内容相同,认为他是同一个人,但是如何让s1.equals(s2)true:针对equals来说比较的是           成员信息内容是否相同;

                重写Object的equals方法同时还需要重写hashCode内容相同,还需要比较哈希码值相同
 
      alt+ins--->hashcode+equals方法
      重写之后,就比较的是成员信息的内容是否相同!
     
克隆方法:
   protected Object clone() throws CloneNotSupportedException:创建对象并返回该对象的副本
   这个方法会抛出一个异常,throws:表示的是可能出现异常,针对调用者必须进行处理

   要使用clone方法,当前某个对象所在的类必须实现"标记接口"Cloneable(没有字段(成员变量),也没有成员方法)
   实现这个接口,那么就可以使用Objectclone()方法

9. Scanner

Scanner:文本扫描器    java.util.Scaner ;

构造方法:
       public Scanner(InputStream source) :创建一个文本扫描器 
       形式参数是一个抽象类--->
       它通过System类里面的public static final InputStream in
          System类中
              public final static InputStream in = null;
              本地方法(Java语言实现)---> private static native void setIn0(InputStream in);
              底层方法一定会创建系统资源---->读取用户输入的 字符(整数,字符串...)
 
Scanner类提供判断功能:  防止出现输入的类型和结果类型不匹配!

public boolean hasNextXXX():判断下一个录入的是否为指定的XXX类型
XXX nextXXX() 获取功能
 
举例:
      public boolean hasNextInt()
      int nextInt()
      如果先录入int,在录入String---->nextLine()---- 录入的字符串数据被漏掉

解决方案;
   1)直接使用next()---->String
   2)在使用nextLine()之前,在创建Scanner对象即可
统一先用String----->全部接收     ----->后期可以通过Integer的特有功能将整数---->String
前提条件:String---->数字字符串 "1","2","3"

举例:
本身:int
录入5个学生的语文,数学,英语成绩,按照总分从高到底排序(可以TreeSet集合进行排序)
语文成绩,数学成绩,英语成绩---->nextLine()---->String
"98"  "78"     "60"
String---    基本类型的包装类类型Integer--->int

八. String

1. String构造方法

java.lang.String:代表的字符串:
字符串是一个常量,一旦被赋值了,其值(地址值)不能被更改
推荐的使用方式:
            String  变量名 = "xxxx" ;//xxxx代表 的当前String的实例
 
String类常用的功能:
获取功能:
int length():获取字符串长

面试题:
在数组中有没有length方法,String类中有没有length方法,在集合中有没有length方法
数组中没有length方法,length属性

               int[] arr = new int[3] ;
               arr.length;
               String类中有length()
               集合中没有length(),----->size()获取元素数
 
构造方法:

(1). public String(): //空参构造:空字符序列
    例: String s = new String() ;
        System.out.println("s:"+s); //String类重写了Object的toString(),
        System.out.println(s.length());
(2). public String(byte[] bytes)://将一个字节数组构造成一个字符串,使用平台默认的字符集(utf-8:一个中文对应三个字节) 解码
    例: byte[] bytes = {97,98,99,100,101} ;
        String s2 = new String(bytes) ;
        System.out.println(s2);
    编码和解码---保证字符集统一
    编码:将一个能看懂的字符串---->字节                "今天老地方见"  utf-8
    解码:将看不懂的字节---->字符串                           "今天老地方见" gbk
 

(3). public String(byte[] bytes,字符集):使用指定的字符集,将字节数组构造成一个字符串
(4). public String(byte[] bytes,int offset,int length):将指定的部分字节数组转换成字符串
                     参数1:字节数组对象,参数2:指定的角标值  参数3:指定长度
     例: String s3 = new String(bytes,2,2) ;
          System.out.println(s3);
          System.out.println(s3.length());
 
(5). public String(char[] value):将字符数组构造成一字符串
     例: char[] chs = {'我','爱','高','圆','圆'} ;
         String s4 = new String(chs) ;
         System.out.println(s4);
         System.out.println(s4.length());
(6). public String(char[] value,int offset,int count):将部分字符数组转换成字符串
    例:  String s5 = new String(chs,1,4) ;
         System.out.println(s5);
         System.out.println(s5.length());
(7). public String(String original):构造一个字符串,参数为字符串常量
    例:  String s6 = new String("hello") ; //创建字符串对象,常量值:hello
         System.out.println(s6);
         String s7 = "hello" ; //推荐的方式
         System.out.println(s7)

//面试题:
   String s1 = "hello" ;
   String s2 = new String("hello") ;
   在内存中分别创建了几个对象?

   第一个创建了一个对象,直接在常量池创建,开辟常量池空间
   第二个:创建了两个对象,一个堆内存中开辟空间,一个指向常量池(不推荐)
   /**
 * String类型重写了Object的equals方法
 * Object 的equals
 *
 *  public boolean equals(Object obj) {
 *         return (this == obj);   //默认比较的地址值
 *     }
 * class String{
 *  private final char value[];  属性 value 字符数组
 * public boolean equals(Object anObject) {  Object anObject = new String() ;
 *         if (this == anObject) {  //判断当前字符串对象和传递进来S2对比
 *             return true;         //判断地址值相同
 *         }
 *         if (anObject instanceof String) {    //判断传进来的s2是否为String类型的实例
 *             String anotherString = (String)anObject; // 向下转型  String类型
 *             int n = value.length;            //获取了字符数组长度   this.value.length---->  int n = 5
 *             if (n == anotherString.value.length) {  //  if(s1的长度 5 == s2.value.length   5)
 *                 char v1[] = value;                  //char v1[] = this.value;  将s1---->v1字符数组
 *                                                      char v1[] = {'h','e','l','l','o'}
 *                 char v2[] = anotherString.value;     //char v2[] =s2.value; 将s2----->v2字符数组
 *                                                      char v2[]  = {'h','e','l','l','o'}
 *                 int i = 0;               //统计变量
 *                 while (n-- != 0) {        // i=0
 *                     if (v1[i] != v2[i])      // v1[0] != v2[0]  ---->'h'     v1[1] != v2[1]
 *                         return false;
 *                     i++;                 /i=1
 *                 }
 *                 return true;         //true
 *             }
 *         }
 *         return false;
 *     }
 *
 *   }
 */ 

2. String类的常用的转换功能: (重点)

(1). byte[] getBytes() : 将字符串转换成字节数组 (编码)
                         如果方法为空参,使用平台默认的编码集进行编码(utf-8:一个中文对应三个字节):   String str = "中国" ;
             byte[] bytes = str.getBytes();//默认utf-8
             System.out.println(Arrays.toString(bytes));//[-28, -72, -83, -27, -101, -67]
(2). byte[] getBytes(String charset): 使用指定的字符集进行编码           
解码的过程:将看不懂的字节数----->String
(3). String(byte[] bytes):使用默认字符集进行解码
       例:    String strResult = new String(bytes) ;// //使用平台默认解码集进行解码:   utf-8
              System.out.println(strResult);
(4). String(byte[] bytes,指定字符集)编码和解码必须要保证字符集统一 
    字符集:
         gbk :一个中文两个字节(中国中文编码表)     
         gb2312:gbk升级版(含义有一个中文字符:中国的中文编码表)     
         iso-8859-1:拉丁文码表     
         utf-8:任何的浏览器--->都支持 utf-8格式 (后期统一个)     
         unicode:国际编码表     
         JS:日本国际 电脑系统 一个字符集
Arrays     
    静态功能:         
    (5). public static String toString(int/byte/float/double...[] a):将任意类型的数组---->String 
    (6). public char[] toCharArray():   将字符串转换成字符数组
               例:               //定义一个字符串
                           String s2 = "helloworldJavaEE" ;
                           // public char[] toCharArray()
                           char[] chs = s2.toCharArray();
                           //遍历字符数组
                            for(int x = 0 ; x < chs.length;  x ++){
                               System.out.println(chs[x]);
                            }
    (7). public String toString():      返回自己本身---"当前字符串的内容":         //System.out.println(s2.toString());
    (8). public String toUpperCase():   将字符串转换成大写
    (9). public String toLowerCase():   将字符串转换成小写java
               例:        System.out.println(s2.toUpperCase());
                          System.out.println(s2.toLowerCase());

3. String类型的判断功能

public boolean equals(Object anObject)://比较两个字符的内容是否相同 (区分大小写)
public boolean equalsIgnoreCase(String anotherString)://比较两个字符串是否相同(不区分大小写)
public boolean startsWith(String prefix)://判断字符串是否以指定的内容开头
public boolean endsWith(String suffix)://判断字符串是否以指定的内容结尾
需求:在某个时间点(今天下午18:00 将某个目录下的所有的以.java文件结尾删除)
          Java中定时器类:Timer---->定时任务TimerTask(抽象类)
          表示文件或者文件夹的抽象路径形式:File类
          递归删除 (定义方法删除)
 
boolean isEmpty()  判断字符串是否为空 ://若为空,则返回true;否则返回false 
String s = "" ;// 空字符串 ,存在String对象 ""
String s = null ; 空值 (空对象) null:引用类型的默认值
    例:
        String s1 = "helloJavaEE" ;
        String s2 = "hellojavaee" ;
        //比较两个字符的内容是否相同 (区分大小写)
        System.out.println("equals:"+s1.equals(s2));
        //比较两个字符的内容是否相同 (不区分大小写)
        System.out.println("equalsIgnoreCase():"+s1.equalsIgnoreCase(s2));
        /*
         *     public boolean startsWith(String prefix):判断字符串是否以指定的内容开头
         *      public boolean endsWith(String suffix):判断字符串是否以指定的内容结尾
         *      boolean isEmpty()  判断字符串是否为空 :若为空,则返回true;否则返回false
         */
        System.out.println("startsWith():"+s1.startsWith("hel"));
        System.out.println("startsWith():"+s1.startsWith("ak47"));
        System.out.println("endsWith():"+s1.endsWith("EE"));
        s1 = "" ; //length()---->长度0 (空字符序列)
        System.out.println(s1.isEmpty());

4. String类的获取功能:(重点)

(1). int length():   //获取字符串长度
(2). public char charAt(int index);  //获取指定索引处的字符:  String str = "helloworldJavaEE" ;
		         System.out.println("charAt():"+str.charAt(4));
(3). public String concat(String str):  //将指定的字符串和当前字符串进行拼接,获取一个新的字符串:  System.out.println("concat:"+str.concat("R").concat("Go"));
(4). public int indexOf(int ch):   //返回指定字符第一次出现的索引值
(5). public int lastIndexOf(int ch):  //返回值指定字符最后一次出现的索引值:   System.out.println("indexOf():"+str.indexOf("o"));
                  System.out.println("lastIndexOf():"+str.lastIndexOf("o"));
(6). public String[] split(String regex): //拆分功能:通过指定的格式将字符串---拆分字符串数组:   String str2 = "JavaEE-Python-Go-R-C-C#-PHP" ;
		          String[] strArray = str2.split("-");
                  for(int x = 0 ; x < strArray.length ; x ++){
                       System.out.print(strArray[x]+"\t");
                  }
(7). public String substring(int beginIndex) ://从指定位置开始默认截取到末尾角标从0开始

(8). public String substring(int beginIndex,int endIndex)://从指定位置开始,截取到位置结束(包前不包右)
                                                          //只能取到endIndex-1处:    String str = "helloworldJavaEE" ;
                   System.out.println("subString():"+str.substring(5));
                   System.out.println("subString():"+str.substring(5,9))
                   
(9). public static String valueOf(boolean/int/long/float/double/char...Object b)//万能方法,将任意类型转换String                                                                                 //类型
                       

5. 字符串其他功能:

(1). public String replace(char target,char replacement)://替换功将指定的内容使用target字符进行替换 
             例:     String s = "helloworld" ;
                     System.out.println("replace():"+s.replace('l','k'));

(2). public String replaceAll(String regex, String replacement) ://将指定的和参数1正则表达式匹配的字符串 使用                                                                        //replacement进行替换

参数1:
     [0-9]   --->如果字符是数字字符

     参数2: "*"替换掉
     
(3). public String trim()://去除字符串两端的空格
         例:        String s3 = "      hello  " ;
                   System.out.println("s3:"+s3.trim()+"----");

重点:
(4). public int compareTo(String anotherString):按照字典顺序比较,返回值是int.
String s1 = "hello" ;
String s2 = "hel" ;
String s3 = "abc" ;
s1.compareTo(s2); //2
s1.compareTo(s3) ;//7
/*
	String类型---内置一个属性: char[] value
   //1)字符串底层---->字符数组  s1和s2--->转换字符串数组 获取长度 5 和 3
     2)通过Math的min方法(5,3) --->获取最小值 int lim = 3 ;
     3) 创建两个数组对象   char[] c1 = {'h','e','l','l','o'}
                         char[] c2 = {'h','e','l'} ;
     4)判断 定义统计变量 k = 0 
     while(k < lim){
     		if(c1[k] != c2[k]){
     				return 对应的字符值进行相减
     				return 'h' - 'a' = 104 - 97 = 7
     		}
     		
     		k ++ ; //统计变量++
     }
     return  字符串的长度相减(字符数组长度相减)
*/

6. int 和 String 类型之间如何转换

//int---->String
   //integer类型的静态功能toString
   public static String toString(int i)
       
 //String--->int
  //Integer的静态功能
       public static int parseInt(String s)
       
  //String ---->Integer ---->int
       String s = "100" ;
	  Integer i = new Integer(s) ;
      int num = i.intValue() ;

7. StringBuffer 的构造方法以及功能

StringBuffer :字符串缓冲区 ---->类似于String,但是不一样 (可变的字符序列)
线程安全------>线程----(多线程中说)
线程依赖于进程存在!进程,能够被系统资源调用的独立单位
一个进程可以有多个线程,每一个线程----->"执行单元"(任务)
线程安全---->同步的----->执行效率低

举例:
银行类的网站/医疗网站
ATM机取钱---->插卡--->输入密码---->查询余额---->取钱
StringBuilder:StringBuffer具有相互兼容的API,它是线程不安全的类---->不同步----->执行效率高

举例:
论坛网站
博客...
单线程程序中:jvm在进行编译的时候  使用StringBuilder去替换StringBuffer
 
//StringBuffer的构造方法:
public StringBuffer() :  空参构造,创建一个空字符序列的字符串缓冲去  (推荐)
public StringBuffer(int capacity): 构造一个字符串缓冲区对象,指定容量大小
public StringBuffer(String str):  指定字符序列,长度加上初始容量16(总容量)
 
//获取功能:
public int length():获取字符数(长度)
public int capacity():获取字符串缓冲区容量
//StringBuffer常用功能
StringBuffer的反转功能:reverse()
StringBuffer的追加功能: append(xxx)
StringBuffer的删除工能:deleteCharAt(int index)delete(int start,int end)
StringBuffer的截取功能: subString(int bengin,int end)
StringBuffer的截取功能: subString(int bengin)
StringBuffer的插入功能: insert(int offset,String str)

8. StringBuffer,StringBuilder的区别

共同点:
	两个都都是字符串缓冲区,支持可变的字符序列!
不同点:
   StringBuffer:线程安全的类---->同步的(多线程:同步锁:源码几乎所有的方法都是同步方法 synchronized)
   				执行效率低
   StringBuilder:线程不安全的类---->不同步----->执行效率高 
   单线程程序中,只考虑执行效率不考虑安全,所以StringBuilder类用作StringBuffer的简易替换
   多线程环境中,要考虑安全问题只能使用StringBuffer,
   
   线程安全的类:StringBuffer,Vector(List接口的子类)

9. StringBuffer和数组的区别

共同点:都是容器,都可以存储任意类型的元素 ,	可以存储基本类型,也可以存储引用类型
数组---->同一种类型的元素   长度是固定的
如果需求中长度是不断变化的,那么数组用不了,考虑:StringBuffer/集合
	
StringBuffer---->存储不同类型的元素 append(int/char/double/Object/foat...)   长度是可变的

借助StringBuffer的功能:reverse()/append()追加----->转换成String形式体现

10. StringBuffer,StringBuilder和String的区别

String:字符串是一个常量,一旦被赋值,其值不能更改/作为形式参数属于特殊的引用类型,形式参数的改变不会实际参数
StringBuffer:可变的字符序列,线程安全的类----同步的----->执行效率低(线程角度)
StringBuilder:可变的字符序列.和StringBuffer具有相互兼容的api,单线程程序中(只考虑执行效率,不考虑安全问题)会使用StringBuilder替代StringBuffer
		作为方法的形式参数,形参的改变会直接影响实际参数

11. String类的遍历

//将字符串的每一个字符分别输出!    charAt(int index)

	String str = "helloJavaEE" ;
	//循环改进: 利用String类的length():获取字符串长度 + charAt(int index)
        for(int x = 0 ;x < str.length() ; x ++){
            System.out.println(str.charAt(x));//charAt(0)
        }
	    //使用String类的转换功能
        //String---->字符数组toCharArray()--->char[]
        char[] chs = str.toCharArray();
        for(int x = 0 ; x < chs.length ; x ++){
            char ch = chs[x] ;
            System.out.println(ch);
        }

13. String类的字符串反转,判断字符串是否为对称字符串(reverse)

public class StringTest4 {
    public static void main(String[] args) {
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in) ;

        //提示并录入数据
        System.out.println("请您输入一个字符串:");
        String line = sc.nextLine() ;
        
        //方式2:使用StringBuffer的功能改进
        boolean flag2 = compare2(line) ;
        System.out.println(flag2);   
        
   	private static boolean compare2(String line) {
       /*
        StringBuffer sb = new StringBuffer(line) ;
        String str = sb.reverse().toString();
        return str.equals(line) ;
        */        
        return new StringBuffer(line).reverse().toString().equals(line) ;
    }

14. String类的equals方法表示什么意思

String类底层已经针对Object的equals和hashCode方法进行了重写
hashCode():String对象的哈希码值是否相同,哈希码值相同的不一定内容就相同,所以它需要覆盖equals方法
比较的字符串内容是否相同.
	String s1 = "hello" ;
	String s2 = new String("hello") ;
	s1 ==s2--->  false 
	s1.equals(s2) ---->true

15. StringBuffer和String如何转换

//String--->StringBuffer
String s = "hello" ;
//使用StringBuffer的有参构造方法
StringBuffer sb = new StringBuffer(s)
//或者是StringBuffer的追加功能
StringBuffer sb = new StringBuffer() ;
sb.append(s) ;

//StringBuffer--->String
//String类的构造方法 String(StringBuffer)
StringBuffer buffe = new StringBuffer("world") ;
String str = new String(buffer) ;
//第二种方式:StringBuffer的toString方法

九. Integer

1. 基本类型对应的包装类类型

整数类型                    引用类型(默认值都是null)
  byte                        Byte
  short                       Short
  int                         Integer
  long                        Long
 *
浮点类型
   float                      Float
   double                     Double

字符类型
   char                       Character
布尔类型
  boolean                     Boolean

2. 通过Integer得到int类型的取值范围

public static String toBinaryString(int i)://将整数---->二进制 的字符串
public static String toOctalString(int i)://将整数---->八进制的字符串
public static String toHexString(int i)://将整数---->十六进制数据
public static final int MAX_VALUE:int的最大值
public static final int MIN_VALUE:int的最小值

        System.out.println(Integer.toBinaryString(100)); // 1100100
        System.out.println(Integer.toOctalString(100));  //  144
        System.out.println(Integer.toHexString(100));    //64

        System.out.println(Integer.MIN_VALUE);//-2的31次方
        System.out.println(Integer.MAX_VALUE);//2的31次方-1

3. Integer的构造方法

Integer(int value):可以将int类型保证为Integer类型
Integer(String s) throws NumberForamtException:  抛出一个数字格式化异常

注:当前字符串如果不是能够解析的整数的,就会出现数字格式化异常,s必须为 数字字符串

        String s  = "50" ;
        Integer integer2 = new Integer(s) ;
        System.out.println(integer2);

4.自动拆装箱

基本类型---> 对应的包装类类型   (装箱)
对应的包装类型---->基本类型     (拆箱)
    
方式:int---->Integer---->String               //Integer作为桥梁
	int i = 50 ;
    Integer ii = new Integer(i) ;
    String str2 = ii.toString(); 
    System.out.println(str2);//50
方式:String ---->Integer---->int
	 String s = "100" ;
	 Integer integer = new Integer(s) ;
     int result2 = integer.intValue();
     System.out.println(result2);//100

> Integer的内部缓存区:IntegerCache   
> 								low =-128   high=127
 直接赋值的形式----》执行的底层Integer.valueOf(int i){}
> Integer i = 128 ;   					//new Integer(128)
> Integer i2 = 128 ;						//new Integer(128)
> System.out.println(i == i2) ;false

十. Charcater : char类型的包装类类型

1. 构造方法

public Character(char value)

        //创建字符类对象
     //Character character = new Character('a') ;
       Character character = new Character((char)(97)) ;
       System.out.println(character); ----------a

2.主要功能

public static boolean isUpperCase(char ch)://判断当前字符是否大写字母字符
public static boolean isLowerCAse(char ch)://是否为小写字母字符
public static boolean isDigit(char ch)://是否为数字字符

3.例题

  //定义三个统计变量
        int bigCount = 0  ;
        int smallCount = 0 ;
        int numberCount =  0;
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in) ;

        //提示并录入数据
        System.out.println("请您输入一个数据:");
        String line = sc.nextLine() ;

        //转换成字符数组
        char[] chs = line.toCharArray();
        for (int i = 0; i <chs.length ; i++) {
            char ch = chs[i] ;
            
         //直接判断
            if(Character.isDigit(ch)){
                numberCount ++ ;
            }else if(Character.isUpperCase(ch)){
                bigCount ++ ;
            }else if(Character.isLowerCase(ch)){
                smallCount ++;
            }
        }

十一. 日历类

1. Data

public Date()://当前系统时间格式
public Date(long date):参数为 时间毫秒值---->Date对象  (197011...)
    
        //创建日期类对象
        Date date  = new Date() ;
        System.out.println(date);

        long time = 60*60 ;
        Date date2 = new Date(time) ;
        System.out.println(date2);

2. Date和String类型如何转换

//java.util.Date---->String: 格式化操作
//1)创建Date对象
Date date = new Date() ;
//2)创建SimpleDateFormat对象
SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd") ;
//3)格式化的操作
String textDate = sdf.foramt(date) ; //使用textDate:日期文本字符串


//String:日期文本字符串----->java.util.Date :解析操作
//1)日期文本字符串
String source = "2021-7-29" ; //格式
//2)创建SimpleDateFormat对象
SimplDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd") ; //模式必须和String的格式一致,否则解析出错
//3)解析操作
Date date2 = sdf2.parse(source) ; //使用Date

3. 静态功能,返回值是它自己本身 public static Calendar getInstance()

public int get(int field)://根据给定日历字段----获取日历字段的值(系统的日历)
     Calendar calendar = Calendar.getInstance();
 	 int year = calendar.get(Calendar.YEAR) ;
	 int month = calendar.get(Calendar.MONTH) ;
	 int date = calendar.get(Calendar.DATE) ;
public abstract void add(int field,int amount)://给指定的日历字段,添加或者减去时间偏移量
                      参数1:日历字段
                      参数2:偏移量
	 int year = calendar.add(Calendar.YEAR,-3);

十二. Random(伪随机数生成器)

1.构造方法

 public Random():    产生一个随机生成器对象,通过成员方法随机数每次没不一样的(推荐)
 public Random(long seed) :参数为long类型的值(随机数流:种子),每次通过成员方法获取随机数产生的随机数相同的

2. 获取随机数的成员方法

 public int nextInt():获取的值的范围是int类型的取值范围(-231次方到231次方-1)
 public int nextInt(int n):获取的0-n之间的数据 (不包含n)
     产生随机数:
               Math类的random方法
                          public static double random();
 
               Random:也能够去使用
                          无参构造方法  + 成员方法
                          public Random():+ public int nextInt(int n)

3. java.lang.Math :针对数学运算的工具类,提供了很多方法

public static int abs(int  a)://绝对值方法
public static double ceil(double a)://向上取整
public static double floor(double a)://向下取整
public static int max(int a,int b)://获取最大值
public static int min(int a,int b)://获取最小值
public static double pow(double a,double b)://a的b次幂
public static double random():[0.0,1.0)://随机数
public static long round(double a)://四舍五入
public static double sqrt(double a)://开平方根
              Math类中的功能都是静态的,里面构造方法私有了!
      一般情况:工具类中构造方法都是会私有化(自定义的工具类),提供对外静态的公共访问方法
      (Java设计模式:单例模式)
JDK5的静态导入特性,必须方法静态的(导入到方法的级别)
     Math类的功能都是静态的,就可以使用静态导入
     import static 包名.类名.方法名;
     前提不能和其他方法名重名;

4. BigDecimal

小数要进行精确计算-还可以计算的同时保留小数点后的有效位数
Java提供的类: BigDecimal
    构造方法:
           public BigDecimal(String value):数字字符串

    成员方法:
           public BigDecimal add(BigDecimal augend)//加
           public BigDecimal subtract(BigDecimal subtrahend)//减
           public BigDecimal multiply(BigDecimal multiplicand)//乘
           public BigDecimal divide(BigDecimal divisor)//除

           public BigDecimal divide(BigDecimal divisor,int scale,int roundingMode)
           参数1:商
           参数2:小数点后保留的有效位数
           参数3:舍入模式 :四舍五入

5. ObjectArrayDemo 对象数组:能够存储对象的数组.

:
需求:
      使用数组存储5个学生(姓名,年龄,性别),然后将数组进行遍历,获取出来每一个学生的信息!
       分析:
               1)创建一个学生类
                       name,age,gender/sex
               2) 数组存储5个学生
                       数组的定义格式:
                       数据类型[] 数组名称 = new 数据类型[长度] ;  学生对象数组
                       数据类型:Student类型  Student[] students = new Student[5] ;
               3)创建5个学生对象:s1,s2,s3,s4,s5
               4)students[0] =s1 ; 给数组中的元素进行赋值
                 students[1] = s2;
                 ....
 
               5)遍历学生数组,获取学生信息
                 现在5个学生,以后学生的不断的增加或减少,用数组合适吗?     数组不适合针对长度可变的需求,所以Java提供---
                 集合框架去使用!
    public class Student {
    private String name ;
    private int age ;
    private String gender ;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}
public class ObjectArrayDemo {
    public static void main(String[] args) {

        //创建学生数组
        //  数据类型[] 数组名称 = new 数据类型[长度] ;  学生对象数组
        Student[] students = new Student[5] ;
        //创建5个学生
        Student s1 = new Student("文章",35,"男") ;
        Student s2 = new Student("高圆圆",42,"女") ;
        Student s3 = new Student("刘亦菲",33,"女") ;
        Student s4 = new Student("马蓉",30,"女") ;
        Student s5 = new Student("马保国",65,"男") ;

        //给数组中的元素赋值
        students[0] = s1 ;
        students[1] = s2 ;
        students[2] = s3 ;
        students[3] = s4 ;
        students[4] = s5 ;

        //遍历学生数组
        for(int x = 0 ; x < students.length ; x ++){
            //System.out.println(students[x]);
            //就需要同getXXX()方法获取成员信息
            Student s = students[x] ;
            System.out.println(s.getName()+"---"+s.getAge()+"---"+s.getGender());
        }
    }
}

十三. Collection

Collection:集合层次的根接口
   一些集合允许元素重复(List),一些集合不允许元素重复(Set)
   一些集合有序(存储和取出一致)(List),一些集合无序(存储和取出不一致)(Set),
   JDK不提供此接口的任何直接实现:它提供了更具体的子接口的实现,如SetList
Collection
     List:
     最具体的子实现类ArrayList,LinkedList,Vector
 基本功能:
      添加
         boolean add(Object e)://添加元素 E(Element)
      删除:
         void clear() //  暴力删除(将集合的素有元素全部干掉)
         boolean remove(Object o)://从集合中删除指定的元素
         获取集合的元素数 :int size()
         判断功能:boolean isEmpty()://判断集合是否为空,为空元素,则返回true
         boolean contains(Object o)://判断集合中是否包含指定元素,包含则返回true

1. Collection的高级功能

boolean addAll(Collection c)://添加一个集合中的所有元素
boolean containsAll(Collection c)://包含一个集合中的所有元素
boolean removeAll(Collection c)://删除集合中的所有元素, (删除一个算删除,还是删除所有)
boolean retainAll(Collection c)://A集合对B集合求交集, boolean的返回值是什么意思,交集的元素是保存在A中还是B//Collection最基本的遍历功能,不属于集合的专有遍历
     Object[] toArray():将集合转换成了对象数组

2. Collection的迭代器:集合的专有遍历方式

Iterator iterator():返回值类型接口类型,需要返回的子实现类对象
    Iterator接口:
 
           boolean hasNext():判断迭代器中是否存在下一个元素
           Object next():  获取下一个可以遍历的元素
           给Collection中存储String类型,遍历出来
               
public class CollectionTest {
    public static void main(String[] args) {
        //创建集合对象
        Collection c = new ArrayList() ; //List接口的子实现类 (重复元素)
        //添加元素
        c.add("hello") ;
        c.add("world") ;
        c.add("javaee") ;
       // System.out.println(c);
        //获取Collection的迭代器Iterator iterator()
        Iterator it = c.iterator();
        //如果现在明确存储了3个元素,以后这些数据可能数据库获取的一个列表集合数据,一般while循环
        while(it.hasNext()){//判断迭代器中有下一个元素
            //才获取
            Object obj = it.next();// Object obj = new String("hello") ...
            //  System.out.println(obj+"----"+obj.length());
            //获取的元素同时,还要获取当前存储元素的长度  ---->String类型   length()
            String str = (String) obj;//向下转型
            System.out.println(str+"----"+str.length());
        }
    }
}    

3.泛型<E/T>

集合类型<引用数据类型> 集合对象名 = new 子实现类<引用数据类型>() ;

泛型的好处:
           1)将运行时期异常提前了编译时期
           2)避免了强制类型转换
           3)提高了程序安全性
    public class GenericDemo {
    public static void main(String[] args) {

        //创建Collection集合对象
        Collection<String> c = new ArrayList<String>() ; //new XXX<数据类型>: jdk7以后泛型推断


        c.add("hello") ;
        c.add("高圆圆") ;
        c.add("你好吗") ;
       // c.add(100) ;

        //获取迭代器Iteratr<E>是集合中存储的泛型是一致的
        Iterator<String> it = c.iterator();
        while(it.hasNext()){
            //获取String字符串的同时,还要获取长度
            String str = it.next();
            System.out.println(str+"---"+str.length());
        }
    }
}

4. Collection的面试题

集合和数组的区别
1)长度区别
       数组:长度固定
       集合:长度可变
 
  2)存储数据类型的区别
       数组:
               可以存储基本数据类型,也可以存储引用数据类型
                  int[] arr = {100,200,50,"hello"} ;不行的
 
        集合:前提条件:集合中加入泛型<>          也是在模拟数组的特点:
               只能存储引用类型  Collection<E> :泛型<引用类型>
  3)存储元素的区别:
              数组:
                       存储的元素必须为同一种数据类型
                               举例:
                                       水杯中加入水
 
              集合:如果没有加入泛型     :就出任意类型的元素(必须引用类型)
 
                               举例:
                                       水杯加入水,可乐,加入威士忌

十四. List

1. List 集合的特点

1.有序(存储元素和取出元素一致)
2.允许元素重复
具备Collection相关的功能
         Object [] toArray()
         Iterator iterator()

2.特有功能

void add(int index,Object element)://在指定的索引处插 入元素
Object get(int index)://获取指定位置处的元素    + int size():一种新的集合遍历方式
Object remove(int index)://删除指定位置处的元素
Object set(int index,E element)://修改指定位置处的元素(替换)
 
     ListIterator<E> listIterator():列表迭代器
                   ListIterator接口:
                           void add(E e)有添加
                           remove():有删除

3. List集合的遍历方式

Object[] toArray()
  Iterator iterator()
  Object get(int index):获取指定位置处的元素    + int size():一种新的集合遍历方式
 
  ListIterator<E> listIterator():列表迭代器  :List集合专有遍历方式
    
public class ListTest {
    public static void main(String[] args) {

        //创建List集合对象
        List<String> list = new ArrayList<>() ;

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");
        list.add("android");

        //size()+get(int index)集合
        for(int x = 0 ; x < list.size() ; x ++){
            String s = list.get(x);
            System.out.println(s+"---"+s.length());
        }

        System.out.println("-----------------------------------------------");
        //List集合存储Student对象并进行遍历(学生:姓名,年龄)
        List<Student> stuList = new ArrayList<>() ;//JDK7以后 泛型推断:自动的和前面的泛型类型一致!

        //创建3个学生
        Student s1 = new Student("张佳宁",31) ;
        Student s2 = new Student("迪丽热巴",29) ;
        Student s3 = new Student("张俊杰",20) ;

        //添加到列表中
        stuList.add(s1) ;
        stuList.add(s2) ;
        stuList.add(s3) ;

        //遍历
        //方式1:Object[] toArray()
        Object[] objs = stuList.toArray();
        for(int x = 0 ; x < objs.length ; x ++){
            Student s = (Student) objs[x];
            System.out.println(s.getName()+"----"+s.getAge());
        }


        System.out.println("--------------------------------------");

        //Collection的迭代器
        Iterator<Student> it = stuList.iterator();
        while(it.hasNext()){
            Student s = it.next() ;
            System.out.println(s.getName()+"---"+s.getAge());
           // System.out.println((it.next().getName()+"---"+(it.next().getAge())));
            // next()只能使用一次,不能多次使用 //错误的用法
        }
        System.out.println("-------------------------------------");

        //方式3:size()+get(int index)
        for(int x = 0 ; x < stuList.size() ; x ++){

            Student s = stuList.get(x);
            System.out.println(s.getName()+"----"+s.getAge());
        }


        System.out.println("-----------------------------------------");

        //正向遍历:ListIterator<E> listIterator():列表迭代器  :List集合专有遍历方式
        ListIterator<Student> lit = stuList.listIterator();
        /**
         * ListIterator extends Iterator{}
         *
         * class ArrayList{
             *  List具体ArrayList子实现类重写了Iterator listiterator(){
             *
             *      return new ListItr(0) ;
             *  }
         *
         *          private class ListItr extends Itr implements Iterator{
         *
         *                      //具备hasNext()
         *                      //next()
         *          }
         *
         *  }
         */
        while(lit.hasNext()){
            Student student = lit.next();
            System.out.println(student.getName()+"---"+student.getAge());
        }

        System.out.println("-----------------------------------------");

        //逆向遍历:前提:必须有正向遍历
        //ListIterator<E> listIterator()
        //ListIterator:特有功能:
        //boolean hasPrevious():是否有上一个元素可以迭代
        //Object previous():获取上一个元素
        while(lit.hasPrevious()){
            Student student = lit.previous();
            System.out.println(student.getName()+"---"+student.getAge());
        }

    }
}

4. LIst如何去重

(1).存储字符串类型
    
方式一:新建空集合思想
//新建一个空的集合List
        List<String> newList = new ArrayList<>() ;
        //遍历以前的集合
        for(String s :list){
            //使用新集合判断,不包含这个元素,说明该元素没有重复,就可以添加
            if(!newList.contains(s)){
                newList.add(s) ;
            }
        }
        //遍历新的集合
        for(String s:newList){
            System.out.println(s);
        }

方式二:利用选择排序的思想去完成
    //利用选择排序的思想完成
        for(int x = 0 ; x < list.size()-1 ; x ++){
            for(int y = x +1 ; y < list.size() ; y++){
                //如果后面的元素和前面的元素相同
                if(list.get(y).equals(list.get(x))){
                    //通过集合remove掉
                    list.remove(y) ; // public Object remove(int index)
                    //角标--
                    y -- ;
                }
            }
        }

        for(String s:list){
            System.out.println(s);
        }

(2). 存储自定义对象
    方式1:新建集合思想
          contains(Object)方法依赖于Object的equals方法,所以集合存储的类型所在的类必须重写equals方法,否则默认使用
    方式2:使用选择排序思想 将List存储的重复的学生对象进行重写!
      
//方式1:创建新的一个新集合
        List<Student> newList = new ArrayList<>() ;
        //遍历以前的集合获取每一个学生对象
        for(Student s:list){
            //如果当前newList不包含这个学生添加到新集合中
            if(!newList.contains(s)){
                newList.add(s) ;
            }
        }

        //遍历新集合
        for(Student student:newList){
            System.out.println(student.getName()+"----"+student.getAge());
        }

5. List的并发修改异常java.util.ConcurrentModificationException

集合在使用迭代器会经常出现的问题:并发修改异常,

当集合的元素正在被迭代器进行遍历,那么集合对象是不能够对元素进行增加或者删除 (一个线程正在遍历,一个线程在修改元素)
解决方案:
     1)要么就是迭代器去遍历集合的元素,迭代器去添加元素 :列表迭代器才具备添加的动作
     2)要么集合遍历,集合添加
public class ListTest2 {
    public static void main(String[] args) {

        //创建List集合对象
        List<String> list = new ArrayList<>() ;

        //给添加元素
        list.add("hello") ;
        list.add("world") ;
        list.add("javaee") ;

        //使用迭代器遍历
        //Iterator iterator()
      /*  Iterator<String> it = list.iterator();  //"hello","world","javaee"
        while(it.hasNext()){
            String s = it.next() ;//"hello","world","javaee"

            //判断
            if("world".equals(s)){
                list.add("javaEE") ;//集合对象添加的元素,迭代器不知道
            }
        }

        System.out.println(list);*/

      //解决方案1:  1)要么就是迭代器去遍历集合的元素,迭代器去添加元素  :列表迭代器才具备添加的动作
        //ListIterator
        //void add(E e)
        /*ListIterator<String> lit = list.listIterator();
        while(lit.hasNext()){
            //获取
            String s = lit.next();
            //判断是否存在"world"元素
            if("world".equals(s)){
                //列表迭代器添加
                lit.add("javaEE");
            }
        }*/

        //方案2:要么集合遍历,集合添加
        //size()+get(int index)
        for(int x = 0 ; x < list.size(); x ++){
            String s = list.get(x);
            if("world".equals(s)){//将常量放前面,防止出现NullPointerException
                list.add("javaEE");
            }
        }
        System.out.println(list);

    }
}

十五. Vector

1. Vector集合特有功能:

添加
     public void addElement(Object obj):在vector对象的末尾添加元素  ------> 一直使用的add(Object e)
删除
     public boolean removeElement(Object obj):删除元素
获取功能
     public Object elementAt(int index):获取指定位置的元素---->类似于  public Object get(int index)
 
     public Enumeration<E> elements() :Vector集合的专有遍历方式---->类似于   Iterator literator()
                       接口
                           boolean hasMoreElements():判断是否有更多的元素可以迭代
                           Object nextElement() 获取元素

2. Vector集合遍历方式

public Enumeration elements()	//Vector集合的专有遍历方式
    
public class VectorDemo {
    public static void main(String[] args) {
        //创建Vector集合对象
        Vector<String> v = new Vector<>() ;
        v.addElement("hello");
        v.addElement("world");
        v.addElement("SpringBoot");
        v.addElement("SpringCloud") ;
        //遍历:特有功能
        Enumeration<String> en = v.elements(); //相当于Iterator
        while(en.hasMoreElements()){
            String s = en.nextElement();
            System.out.println(s+"---"+s.length());
        }
        System.out.println("----------------------------------");
        for(String s: v){
            System.out.println(s+"----"+s.length());
        }

    }
}

十六. 增强for循环

1. 增强for循环

替代集合中迭代器去遍历集合使用的(优先在集合中使用)

格式:
    for(存储的引用数据类型 变量名: 集合/数组对象){
    //集合使用居多,数组一般都是使用普通for
    使用变量名即可
    }
注意事项:
       当前集合对象不能为空 null :foreach语句:增强for它本身就是获取迭代器了,就会出现空指针异常
       
public class ForeachDemo {
    public static void main(String[] args) {

        //数组
        int[] arr = {11,22,33,44,55} ;
        for(int x  = 0 ; x < arr.length ; x ++){
            System.out.println(arr[x]);
        }
        System.out.println("--------------------------------");

        /*
        for(存储的引用数据类型 变量名: 集合/数组对象){
        *            使用变量名即可
                    *      }

         */
        //对于数组来说:使用普通for
       /* for(int a:arr){
            System.out.println(a);
        }*/

       //创建List集合
        List<String> list = new ArrayList<>() ;
        list.add("hello") ;
        list.add("world") ;
        list.add("javaee") ;

       /* for(String s:list){//替换迭代器使用
            //如果存在world元素,添加一个android元素
            //System.out.println(s);

            if("world".equals(s)){
                list.add("android") ;//出现并发修改异常
            }
           }
           System.out.println(list);
        */

       list = null ;
       if(list!=null){
           for(String s:list){//获取迭代器
               System.out.println(s+"---"+s.length());
           }
       }else{
           System.out.println("当前集合对象为null了");
       }


    }
}

2. 面试题

需求:
    使用增强for遍历List,存储三个学生,遍历后获取学生信息(姓名和年龄)
public class ForeachTest {

    public static void main(String[] args) {

        //创建List
        List<Student> list = new ArrayList<>() ;
        //创建三个学生
        Student s1  = new Student("张佳宁",29) ;
        Student s2  = new Student("邓超",40) ;
        Student s3  = new Student("黄海波",30) ;

        list.add(s1) ;
        list.add(s2) ;
        list.add(s3) ;
        //方式5:增强for循环
        for (Student s: list) {
//           System.out.println(s.getName()+"---"+s.getAge());
            System.out.println(s); //对象名称:直接使用重写后的toString()
        }
    }
}

3. 插入排序

核心思想:使用1角标对应的元素进行和0角标比较
        如果前面元素大,向右移动,确定角标1对应的元素的位置,再次使用2角标对应的元素依次和10都元素比较
        依次这样比较
public class InsertSortTest {

    public static void main(String[] args) {

        //定义一个Integer数组: Integer实现的自然排序:元素能够按照升序默认排序
        Integer[] arr = {34,8,64,51,32,21} ;

        System.out.println("排序前:");
        printArr(arr);

        //定义一个功能
        insertSort(arr) ;
        System.out.println("排序后:");
        printArr(arr);

    }
    //插入排序
    private static void insertSort(Integer[] arr) {

        //定义一个变量j
            int j ; //j记录当前角标的变化
            //定义变量 : p:表示一个比较次数 p=1,2,3,4,5 (每一移动的元素的位置)
            for(int p = 1 ; p < arr.length ; p ++ ){ //比较次数     p=2
                //定义临时变量temp
                Integer temp = arr[p] ;    //temp = 8;          temp = 64
                //开始比较
                for(j = p ; j>0 && temp.compareTo(arr[j-1])<0; j-- ){  // j= 1 ; 1>0&& 8 < 34   j-- : j= 0
                    //j=2 ; 2>0 && 64 < 32
                    //数据移动
                    arr[j] = arr[j-1] ;
                }

                //确定temp的位置:8的位置   64的位置:p=2
                arr[j] = temp ;         // 没有移动

        }

    }

    public static void printArr(Integer[] arr){
        System.out.print("[");
        for(int x = 0 ; x < arr.length ; x ++){
            if(x == arr.length -1){
                System.out.println(arr[x] +"]");
            }else{
                System.out.print(arr[x]+", ");
            }
        }
    }
}

十七. LinkedListDemo

1. LinkedList集合特点:

线程不安全的类,执行效率高链接列表结构,查询慢,增删快

2.特有功能:

public void addFirst(Object e):在列表开头插入元素
public void addLast(Object e):将元素追加到列表的末尾

public Object getFirst():获取列表的第一个元素
public Object getLast():获取列表的最后一个元素

public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素
public Object removeLast():删除列表的最后一个元素,并获取最后一个元素

十八. HashSet

1. HashSet

 Set集合:无序(存储和取出不一致),能够保证元素唯一

 HashSet:底层数据结构是一个哈希表(桶结构)
    线程不安全的类---->不同步---->执行效率高
 
 JDK8以后;提供了juc(并发包:java.util.concurrent):
    ConcurrentHashMap<K,V>和HashMap的区别 ?

 String类型:String类型本身已经重写了hashCode()和equals,如果hashCode和equals()都相同,
    那么认为同一个元素,存储以前的值

2. 如果现在存储是自定义对象,如何保证元素唯一?HashSet

 
    Student s1 = new Student("高圆圆"42) ;
    Student s2 = new Student("高圆圆"42) ;
 
    HashSet集合依赖于add方法---->HashMap的put方法
 
    首先要比较元素的哈希码值相同----->hash()就相同
    还要比较成员信息是否相同,对应存储自定的类必须要重写Object的equals方法
 
    Student的这个类,必须手动给出hashCode()和equals
 
    Hashset集合不能保证顺序迭代恒久不变!
 
    应用场景:
           在一些需求中,如果没有明确要求元素重复,那就可以使用hashSet,保证元素唯一!
                       类型:String,Integer,Long,....
                    
   例题:
    public class Student {
    private String name ;//姓名
    private int age ;//年龄
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name.equals(student.name);
    }
    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }
}
public class HashSetDemo2 {

    public static void main(String[] args) {
        
        //创建HashSet集合对象
        HashSet<Student> hs1 = new HashSet<>() ;

        Student s1 = new Student("宋江",35) ;
        Student s2 = new Student("宋江",35) ;
        Student s3 = new Student("武松",30) ;
        Student s4 = new Student("宋江",30) ;
        Student s5 = new Student("武松",30) ;
        Student s6 = new Student("卢俊义",28) ;
        Student s7 = new Student("卢俊义",28) ;
        System.out.println("-------------------------------");
        //System.out.println(s1.hashCode());
        //System.out.println(s2.hashCode());

        //添加集合中
        hs1.add(s1) ;
        hs1.add(s2) ;
        hs1.add(s3) ;
        hs1.add(s4) ;
        hs1.add(s5) ;
        hs1.add(s6) ;
        hs1.add(s7) ;

        //遍历
        for(Student s : hs1){
            System.out.println(s.getName()+"---"+s.getAge());
        }
    }
}


3. ArrayList的嵌套

  ArrayList<ArrayList<Studet>>
  ArrayList<Studet>代表一个java基础班,同时三个班级,都是可以三个ArrayList<Studet>来表示
 
  ArrayList<ArrayList<Studet>> --->  三个ArrayList<Studet>       每一个ArrayList<Student>存储三个学生
  将大集合进行遍历!
    
    
  public class Student {
    private String name ;//姓名
    private int age ;//年龄
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class ArrayListIncludeArrayListTest {

    public static void main(String[] args) {
        //需要创建一个大的集合
        ArrayList<ArrayList<Student>>  bigArray = new ArrayList<>() ;

        //第一个子集合ArrayList<Student>
        ArrayList<Student> firArray = new ArrayList<>() ;
        Student s1 = new Student("高圆圆",42) ;
        Student s2 = new Student("文章",35) ;
        Student s3 = new Student("王宝强",30) ;
        firArray.add(s1) ;
        firArray.add(s2) ;
        firArray.add(s3) ;

        //将第一个子集合添加到大集合中
        bigArray.add(firArray) ;

        //第二个子集合ArrayList<Student>
        ArrayList<Student> secArray = new ArrayList<>() ;
        Student s4 = new Student("张三",42) ;
        Student s5 = new Student("王五",35) ;
        Student s6 = new Student("李四",30) ;
        secArray.add(s4) ;
        secArray.add(s5) ;
        secArray.add(s6) ;

        //将第二个子集合添加到大集合中
        bigArray.add(secArray) ;

        //第三个子集合ArrayList<Student>
        ArrayList<Student> thirArray = new ArrayList<>() ;
        Student s7 = new Student("盲僧",42) ;
        Student s8 = new Student("亚索",35) ;
        Student s9 = new Student("提莫",30) ;
        thirArray.add(s7) ;
        thirArray.add(s8) ;
        thirArray.add(s9) ;

        //将第三个集合添加到集合中
        bigArray.add(thirArray) ;

        //ArrayList<ArrayList<Student>>遍历大集合
        for(ArrayList<Student> array:bigArray){
            for(Student s: array){
                System.out.println(s.getName()+"\t"+s.getAge());
            }
        }
    }
}

4. Treeset

 TreeSet集合 : 无序性,元素唯一

  底层依赖于TreeMap集合,  红黑树结构(也称为 "自平衡的二叉树结构"),可以实现Map的自然排序以及比较器排序取决于使用的构造方法

  构造方法:
       public TreeSet():构造一个空的树,实现元素自然排序 (取决于存储的元素类型能否实现Comparable接口)

       自然排序 ----->执行的TreeSet无参构造方法,而且前提条件当前存储类型必须实现Comparable接口  
TreeSet能够实现两种排序:

  自然排序/比较器排序,取决于构造方法
  自然排序:TreeSet<E>(),E类型必须实现Comparable接口,实现自然排序(实现的compareTo(T t))
 
  比较器排序:
         public TreeSet(Comparator<? super E> comparator)
 
                Comparator是一个接口类型
                       1)自定义一个类实现Comparator接口,重写compare方法
                       2)使用接口的匿名内部类(推荐)
 
 
               使用TreeSet存储Student类型,遍历元素(使用比较器排序完成)
     排序条件:
            主要条件:按照学生的年龄从小到大排序

5. 自然排序

例题: 

使用TreeSet存储Student类型,遍历元素
    排序条件:
           主要条件:按照学生的年龄从小到大排序,自己分析次要条件
               
//实现自然排序 实现一个接口Comparable
public class Student implements  Comparable<Student>{

    private String name ;//姓名
    private int age ;//年龄

    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    //排序的代码
    @Override
    public int compareTo(Student s) { //后面需要和学生对象对比

        //主要条件:就是按照年龄从小到大排序
        //定义一个变量
        //年龄int类型
       /* int num = this.age - s.age ;
       // int num = s.age - this.age ;//从大到小

        //次要条件:年龄相同,还要比较姓名的内容是否相同
        int num2 = (num==0)?(this.name.compareTo(s.name)):num;    //字符串的字典顺序比较
                            //gaoyuanyuan
                            //jacky
        return num2;*/

      // 第二种情景     主要条件:按照学生姓名的长度:从小到大进行排序
        
       //主要条件:按照学生姓名的长度:从小到大进行排序
        int num = this.name.length() - s.name.length() ;

        //如果长度相同,还比较内容是否一样          "hello" ,"hel"
        int num2 = (num==0)?(this.name.compareTo(s.name)):num ;

        //如果长度相同,内容一样
        //按照学生的年龄从小到大比
        int num3 = (num2==0)? (this.age - s.age) :num2 ;
        return num3 ;
    }
}

public class TreeSetDemo2 {
    public static void main(String[] args) {

        //创建TreeSet,无参构造方法
        TreeSet<Student> ts = new TreeSet<>() ;

        //创建几个学生对象
        Student s1 = new Student("gaoyuanyuan",42) ;
        Student s2 = new Student("gaoyuanyuan",42) ;
        Student s3 = new Student("jacky",40) ;
        Student s4 = new Student("rose",40) ;
        Student s5 = new Student("tomcat",35) ;
        Student s6 = new Student("jeffry",35) ;
        Student s7 = new Student("liushishi",54) ;
        Student s8 = new Student("liudehua",60) ;

        //添加
        ts.add(s1) ;        //因为当前集合存储的自定义对象,元素要实现自然排序必须所在的类实现Compareable接口
        ts.add(s2) ;
        ts.add(s3) ;
        ts.add(s4) ;
        ts.add(s5) ;
        ts.add(s6) ;
        ts.add(s7) ;
        ts.add(s8) ;

        //遍历
        for(Student s : ts){
            System.out.println(s.getName()+"---"+s.getAge());
        }

    }
}

6. 比较器排序

例题2:

//比较器排序

public class Student {
    private String name ;//姓名
    private int age ;//年龄
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name.equals(student.name);
    }
    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }
}
        
    //方式1:接口子实现类对象

    public class MyComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        //主要条件:按照学生的年龄从小到大排序
        //s1---->就是刚才自然排序里面this
        //s2---->就是刚才自然排序里面s
        int num = s1.getAge() - s2.getAge() ;
        //如果年龄相同,比较姓名是否一样
        int num2 = (num==0)? (s1.getName().compareTo(s2.getName())): num ;
        return num2;
    }
}

    //方式2:接口的匿名内部类

    public class TreeSetDemo {
    public static void main(String[] args) {
    TreeSet<Student>  ts = new TreeSet<>(new Comparator<Student>() {
        
            @Override
            public int compare(Student s1, Student s2) {
                //主要条件:按照学生的年龄从小到大排序
                //s1---->就是刚才自然排序里面this
                //s2---->就是刚才自然排序里面s
                int num = s1.getAge() - s2.getAge() ;
                //如果年龄相同,比较姓名是否一样
                int num2 = (num==0)? (s1.getName().compareTo(s2.getName())): num ;

                return num2;
            }
        }) ;

        //创建几个学生对象
        Student s1 = new Student("gaoyuanyuan",42) ;
        Student s2 = new Student("gaoyuanyuan",42) ;
        Student s3 = new Student("jacky",40) ;
        Student s4 = new Student("rose",40) ;
        Student s5 = new Student("tomcat",35) ;
        Student s6 = new Student("jeffry",35) ;
        Student s7 = new Student("liushishi",54) ;
        Student s8 = new Student("liudehua",60) ;

        ts.add(s1) ;
        ts.add(s2) ;
        ts.add(s3) ;
        ts.add(s4) ;
        ts.add(s5) ;
        ts.add(s6) ;
        ts.add(s7) ;
        ts.add(s8) ;


        for (Student s:ts) {
            System.out.println(s.getName()+"---"+s.getAge());
        }

    }
}

7. 泛型

泛型高级通配符(了解)
    <?>    :任意Java类型,包括Object
    <? super E> : 向上限定:E类型以及E父类
    <? extends E>: 向下限定:E以及它的子类
    
      //创建Collection
        Collection<?> c1 = new ArrayList<Object>() ;
        Collection<?> c2 = new ArrayList<Animal>() ;
        Collection<?> c3 = new ArrayList<Cat>() ;
        Collection<?> c4 = new ArrayList<Dog>() ;
        System.out.println("------------------------------");
        Collection<? super  Cat> c5 = new ArrayList<Cat>() ;//最基本的一致
        Collection<? super  Cat> c6 = new ArrayList<Animal>() ;
        Collection<? super  Cat> c7 = new ArrayList<Object>() ;
        System.out.println("------------------------------");
        Collection<? extends   Object> c8 = new ArrayList<Object>() ;
        Collection<? extends   Object> c9 = new ArrayList<Animal>() ;
        Collection<? extends   Object> c10 = new ArrayList<Cat>() ;
        Collection<? extends   Animal> c11 = new ArrayList<Animal>() ;
        Collection<? extends   Animal> c12 = new ArrayList<Cat>() ;
        Collection<? extends   Animal> c13 = new ArrayList<Dog>() ;
        Collection<? extends   Animal> c14 = new ArrayList<Object>() ;

十九. Map

1. Map

  Map集合:键映射到值的对象,Map集合可以多个值,但键必须唯一!
  
  特点:
    每个元素成对存在,由键和值两部分组成,通过键可以找到对应的值
    键(key值)不可重复,值(value)可以重复,一个value值可以和很多key值形成对应关系,每个建最多只能映射到一个值
    两个 Map 接口的重要实现类:HashMap类、LinkedHashMap 类	
    

2. Map子类(HashMap HashTable TreeMap LinkedHashMap )

1.HashMap
    使用 HashMap定义的Map集合是无序存放的
    如果发现重复的 key会将新的数据替换掉已有的数据
    使用 HashMap子类保存数据时,key或 value可以保存为null
    HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap
    
2.HashTable    
    Hashtable和 HashMap很相似,不同之处是 Hashtable线程是安全的,key不允许设置为 null
    
3.TreeMap 
    可以排序的Map集合,按集合中的key排序,key不允许重复
    最终保存在Map中的数据是经过排序的数据,按其key排序
    
4.LinkedHashMap    
    LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,因此在遍历的时候会比HashMap效率要低。
	不过也有例外情况,当HashMap容量很大,实际存储的数据较少时,遍历起来可能会比LinkedHashMap要慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关
	一般情况下,在Map中插入、删除和定位元素,HashMap 是最好的选择。如果需要元素输出的顺序和输入的相同,就需要选择LinkedHashMap了

3. Map集合的功能

Map集合的功能:
      1)V put(K key,V value):添加键值对元素
               注意事项:
                       如果key是第一次添加,那么返回的结果为null
                       如果key是否重复添加,第二次添加,返回的上一次添加的键对应的值                 
      2)V  remove(Object key):删除指定的键,返回被删除键对应的值
      3)void clear()
      4)boolean containsKey(Object key) :是否包含指定的键 (使用居多)
      5)boolean containsValue(Object value):是否包含指定的值

4. HashMap 和 HashTable 的区别

共同点:底层数据结构是桶结构(基于哈希表实现)
不同点:
1)HashMap是线程不安全的类,多线程下会造成并发冲突,但单线程下运行效率较高
   HashTable是线程安全的类,很多方法都是用synchronized修饰,但同时因为加锁导致并发效率低下,单线程环境效率也十分低
2)插入null:HashMap允许有一个键为null,允许多个值为null
            HashTable不允许键或值为null
3)容量:HashMap底层数组长度必须为2的幂,这样做是为了hash准备,默认为16
        HashTable底层数组长度可以为任意值,这就造成了hash算法散射不均匀,容易造成hash冲突,默认为11
4)Hash映射:HashMap的hash算法通过非常规设计,将底层table长度设计为2的幂,使用位与运算代替取模运算,减少运算消耗
            HashTable的hash算法首先使得hash值小于整型数最大值,再通过取模进行散射运算

5. Map 和 Collection集合的区别

MapCollection集合的区别:
          Collection:只能存储一种类型 Collection<E>            简单记"光棍"
          Map集合:可以两种类型的,键的类型,值的类型 Map<K,V>       简单记"夫妻对"
 
          遍历方式不同
           Collection:就通过5种方式(List)
           Map:两种方式:
                   方式1:获取所有的K的集合(键的集合)
                           通过键获取值
                   方式2: 获取所有的键值对对象Map.Entry<K,V> ("结婚证")
                           通过键值对对象获取所有的键("结婚证男方")
                           通过键值对对象获取所有的值("结婚证女方")
 
   有内在联系:
           TreeSet集合---->Collection---->间接的使用到了TreeMap集合的put方法
           HashSet------>Collection---->间接使用到了HashMap的put方法
public class MapDemo {
    public static void main(String[] args) {
        //创建Map集合:接口
        //默认用的是HashMap集合        TreeMap(根据元素排序)
        Map<String,String> map = new HashMap<String,String>() ;
        System.out.println(map);
        //添加功能
       // String result = map.put("文章", "马伊琍");
      //  System.out.println(result);
        map.put("文章", "马伊琍");
        map.put("王宝强","马蓉") ;
        map.put("杨过","小龙女") ;
        map.put("郭靖","黄蓉") ;
       // String result2 = map.put("文章", "姚笛");
      //  System.out.println(result2);
        map.put("文章", "姚笛");
        System.out.println("---------------------------------");
       // System.out.println(map.remove("杨过"));
       // map.clear();
        System.out.println(map.containsKey("周杰伦")) ;
        System.out.println(map.containsKey("王宝强")) ;
        System.out.println(map.containsValue("郭蓉")) ;
        System.out.println(map.containsValue("小龙女")) ;
        System.out.println(map);
    }
}

6. 遍历

高级功能:
	Map遍历功能
		  方式1:
             Set<K> keySet()  :获取当前Map集合中的所有的键的集合  (将所有的丈夫集中起来,找对应的妻子)
              +
              V get(Object key):通过键获取值
          方式2:
              获取所有的结婚证  (键值对对象)
                 Set<Map.Entry<K,V>> entrySet()
                    通过键值对象 获取键 /获取值(通过结婚证找男方/女方)
                        K getKey()
                        V getValue()

public class MapDemo2 {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<String,String> map = new HashMap<>() ;
        //添加元素
        map.put("令狐冲","东方不败") ;
        map.put("杨过","小龙女") ;
        map.put("陈玄风","梅超风") ;
        map.put("郭靖","黄蓉") ;
        // Set<K> keySet()  :获取当前Map集合中的所有的键的集合
        Set<String> keySet = map.keySet(); //推荐第一种方式
        //增强for遍历
        for(String key: keySet){
            //获取所有的键的元素
            //  V get(Object key):通过键获取值
            String value = map.get(key);
            System.out.println(key+"="+value);
        }
        System.out.println("--------------------------------------");
        //方式2:
        //Set<Map.Entry<K,V>> entrySet()
        Set<Map.Entry<String, String>> entry = map.entrySet();
        //增强for:遍历键值对对象获取到
        for(Map.Entry<String, String> en: entry){
            //获取键和值
            //K getKey()
           // V getValue()
            String key = en.getKey();
            String value = en.getValue();
            System.out.println(key+"="+value);
        }
    }
}

7. HashMap

HashMap<Student,String>:         HashMap<String,Student>
    Key:  Student类型(姓名和年龄):自定义类型
    Value:  String(爱好)
    Map集合针对键有效:不能迭代顺序恒久不变
    HashMap的put方法依赖于hashCode()和equals方法,键的类型必须重写Object类的hashCode和equals方法,保证键唯一!

//public class Student  implements Comparable<Student>{
public class Student{
    private String name ;
    private  int age ;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
    /*@Override
    public int compareTo(Student s) {

        //主要条件:学生的年龄从小到大排序
        int num = this.age - s.age ;
        //如果年龄相同,要比较姓名的内容是否相同
        int num2 = (num==0)? (this.name.compareTo(s.name)):num ;
        return num2;
    }*/
}
public class HashMapDemo {
    public static void main(String[] args) {
        //创建Map集合对象
        HashMap<Student,String> map = new HashMap<>() ;
        //创建几个学生对象
        Student s1 = new Student("文章",35) ;
        Student s2 = new Student("文章",35) ;
        Student s3 = new Student("文章",37) ;
        Student s4 = new Student("潘玮柏",40) ;
        Student s5 = new Student("赵又廷",39) ;
        Student s6 = new Student("蔡徐坤",38) ;
        Student s7 = new Student("蔡徐坤",38) ;
        Student s8 = new Student("肖战",30) ;
        map.put(s1,"足球") ;
        map.put(s2,"篮球") ;
        map.put(s3,"足球") ;
        map.put(s4,"吸毒") ;
        map.put(s5,"高圆圆") ;
        map.put(s6,"乒乓球") ;
        map.put(s7,"篮球") ;
        map.put(s8,"演戏") ;
        //遍历
        Set<Student> students = map.keySet();
        for(Student key :students){
            //通过键获取值
            String hobit = map.get(key);
            System.out.println(key.getName()+"---"+key.getAge()+"---"+hobit);
        }
    }
}

8. TreeMap

TreeMap:红黑树结构---针对Map的键按照条件排序---键属于自定义的情况
       TreeMap<Integer,String>
       TreeMap<Student,String>
       存储学生类型(姓名,年龄) ,value:描述"朝代"
       键必须唯一而且排序的主要条件:按照学生的年龄从小到大排序
TreeMap的构造方法
       public TreeMap():针对键进行自然排序
       public TreeMap(Comparator<? super K> comparator):针对键按照比较器进行排序
public class TreeMapDemo {
    public static void main(String[] args) {
        //创建TreeMap集合对象
        //TreeMap<Student,String> tm = new TreeMap<>() ; 
        //无参构造方法:自然排序  :前提条件:键 的类型必须实现Comparable
        //比较器排序:匿名内部类
        TreeMap<Student,String> tm = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //主要条件:学生的年龄从大到小排序
                int num = s2.getAge() - s1.getAge() ;
                //如果年龄相同,要比较姓名的内容是否相同
                int num2 = (num==0)? (s1.getName().compareTo(s2.getName())):num ;
                return num2;
            }
        }) ;
        //创建学生对象
        Student s1 = new Student("唐伯虎",38) ;
        Student s2 = new Student("唐伯虎",38) ;
        Student s3 = new Student("秋香",30) ;
        Student s4 = new Student("祝枝山",40) ;
        Student s5 = new Student("祝枝山",45) ;
        Student s6 = new Student("文征明",39) ;
        Student s7 = new Student("石榴姐",20) ;
        Student s8 = new Student("东香",18) ;
        Student s9 = new Student("徐香",18) ;
        tm.put(s1,"明朝") ;
        tm.put(s2,"宋代") ;
        tm.put(s3,"清朝") ;
        tm.put(s4,"明朝") ;
        tm.put(s5,"现代") ;
        tm.put(s6,"唐朝") ;
        tm.put(s7,"宋代") ;
        tm.put(s8,"明朝") ;
        tm.put(s9,"现代") ;
        Set<Student> students = tm.keySet();
        for(Student key :students){
            String value = tm.get(key);
            System.out.println(key.getName()+"---"+key.getAge()+"---"+value);
        }
    }
}

二十. Collection

1. 针对集合操作工具类

Collections:针对集合操作工具类
    提供静态功能:
        1public static <T extends Comparable<? super T>> void sort(List<T> list)//按照自然升序排序(针对List集合排序)
        2public static <T> void sort(List<T> list,Comparator<? super T> c)//按照比较器排序针对List集合
        3public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>:
              //获取当前自然顺序中List的最大值
        4public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T>:
              //最小值
        5public static void reverse(List<?> list)//对List集合顺序反转
        6public static void shuffle(List<?> list)//随机置换
public class CollectionsDemo1 {
    public static void main(String[] args) {
        //创建List集合
        List<Integer> list = new ArrayList<>() ;
        //添加元素
        list.add(10) ;
        list.add(50) ;
        list.add(15) ;
        list.add(25) ;
        list.add(5) ;
        list.add(12) ;
        System.out.println(list);
        System.out.println("---------------------------------");
        //public static <T extends Comparable<? super T>> void sort(List<T> list):
        Collections.sort(list);
        System.out.println(list);
        System.out.println("----------------------------------");
        //public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>
        Integer max = Collections.max(list);
        System.out.println(max);
        System.out.println("----------------------------------");
        System.out.println(Collections.min(list));
        System.out.println("-----------------------------------");
        Collections.reverse(list);//反转
        System.out.println(list);
        System.out.println("-----------------------------------");
        //  public static void shuffle(List<?> list):随机置换
        Collections.shuffle(list);
        System.out.println(list);
    }
}

2. 自定义类型元素

  1)public static <T extends Comparable<? super T>> void sort(List<T> list):按照自然升序排序(针对List集合排序)
  2)public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
     List<Student>:针对List进行自然升序排序,
           主要条件:按照学生的年龄从小到大排
 		   次要条件:年龄相同,比较姓名内容是否相同!

//public class Student implements Comparable<Student> {
public class Student {
    private String name ;
    private int age ;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
   /* @Override
    public int compareTo(Student s) {

        //主要条件:按照学生的年龄从小到大排序
        int num = this.age - s.age ;
        //如果年龄相同,姓名比(字典顺序)
        int num2 = (num==0)?(this.name.compareTo(s.name)):num ;
        return num2;
    }*/
}

public class CollectionsTest {
    public static void main(String[] args) {
        //创建List集合对象
        List<Student> list = new ArrayList<>() ;
        //创建几个学生对象
        Student s1 = new Student("gaogaoyuan",42) ;
        Student s2 = new Student("gaogaoyuan",40) ;
        Student s3 = new Student("liushishi",42) ;
        Student s4 = new Student("wanglihong",45) ;
        Student s5 = new Student("wenzhang",38) ;
        Student s6 = new Student("huazi",35) ;
        Student s7 = new Student("huazi",32) ;
        Student s8 = new Student("zhangjunjie",20) ;
        //添加
        list.add(s1) ;
        list.add(s2) ;
        list.add(s3) ;
        list.add(s4) ;
        list.add(s5) ;
        list.add(s6) ;
        list.add(s7) ;
        list.add(s8) ;
        //排序
        //Collections.sort(list); //自然升序排序:针对集合当前存储的类型必须实现Comparable
        //使用比较器排序:针对List集合
        //public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num  = s1.getAge() - s2.getAge() ;
                int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
                return num2;
            }
        });
        for(Student s:list){
            System.out.println(s.getName()+"---"+s.getAge());
        }
    }
}

3. 斗地主

/* 
	模拟斗地主,保证牌有序
 */
public class PokerTest2 {
    public static void main(String[] args) {
        //1)牌盒
        //创建一个牌盒Map:HashMap<Integer,String> key:编号  value:牌
        HashMap<Integer,String> hm = new HashMap<>();
        //创建一个ArrayList集合:存储编号
        ArrayList<Integer> arrayList = new ArrayList<>() ;
        //2)装牌
        //创建点数数组
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"} ;
        //创建花色数组
        String[] colors = {"♥","♠","♣","♦"} ;
        //拼接
        //定义牌的编号:0开始
        int index = 0 ;
        for(String number:numbers){
            for(String color:colors){
                String porker = number.concat(color);
                //将编号以及牌都添加HashMap集合
                hm.put(index,porker) ;
                //单独给ArrayList存储编号
                arrayList.add(index) ;
                index ++ ;
            }
        }
        //给HashMap集合添加小王,大王,给ArrayList添加小王,大王的编号
        hm.put(index,"小王") ;
        arrayList.add(index) ;
        index ++ ;
        hm.put(index,"大王") ;
        arrayList.add(index) ;
      //  System.out.println(arrayList);
        //3)洗牌:随机置换 ArrayList<Integer> 洗的是编号
        Collections.shuffle(arrayList);
      //  System.out.println(arrayList);
        //4)发牌
        /*
        为了保证牌有序,发也是编号

         *   三个人都分别是 TreeSet<Integer>集合
         *             创建一个集合:diPai
         *               判断:
         *                   如果角标>=牌盒整个size()-3   底牌
         *                   如果角标 %3 == 0    第一个人的
         *                   如果角标 %3 == 1    第二个人的
         *                           %3  == 2    第三个人的
         */
        TreeSet<Integer> player1 = new TreeSet<>() ;
        TreeSet<Integer> player2 = new TreeSet<>() ;
        TreeSet<Integer> player3 = new TreeSet<>() ;
        //底牌
        TreeSet<Integer> diPai = new TreeSet<>() ;
        for(int x = 0 ;x < arrayList.size() ; x ++){
            //0开始
            if(x >= arrayList.size()-3){
                diPai.add(arrayList.get(x)) ;
            }else if(x % 3 == 0 ){
                player1.add(arrayList.get(x)) ;
            }else if(x % 3 == 1){
                player2.add(arrayList.get(x)) ;
            }else if(x % 3 ==2){
                player3.add(arrayList.get(x)) ;
            }
        }
        //看牌://看牌:每一个人都可以看牌,还可以看底牌,所以看牌封装一个功能
        lookPoker("张俊杰",player1,hm);
        lookPoker("高圆圆",player2,hm);
        lookPoker("赵又廷",player3,hm);
        lookPoker("底牌",diPai,hm);
    }
    public static void lookPoker(String name,TreeSet<Integer> ts,HashMap<Integer,String> hm){
        //玩家1的牌是:xxx...
        //玩家2的牌是:xxx....
        System.out.print(name+"的牌是:");
        //遍历TreeSet集合,获取每一个编号
        for(Integer key: ts){
            //获取到每一个编号---在HashMap集合中属于key(键) 编号
            String poker = hm.get(key); //在大Map集合中通过键获取值
            System.out.print(poker+" ");
        }
        System.out.println();
    }
}

二十一. 异常

1. 异常

1.Throwable:包含所有的错误以及异常! 它是一个超类(父类)
       error , Exception

       1)error:非常严重问题  (跟代码没有太大有关系)
              OOM Out Of Memory:内存溢出 (严重问题)
                 举例:
                       手机移动端  (旅游app)
                       下拉刷新的速度比图片还快(一个activity:界面 一次性刷新很多图片)
       2Exception:异常
                编译时期异常和运行时期异常(RuntimeException):程序在运行过程中出现问题(代码书写不严谨)
                只要不是RuntimeException的子类都是属于编译时期异常
 
          error---->  在生活中  "地震了,不可抗力的因素"
          Exception:异常
                   编译时期异常:  在程序,运行前需要检查的! 在生活中 "长途旅行之前,检查你的车胎情况"...
                   运行时期异常:在程序.程序代码逻辑问题(代码不严谨)  在生活中  "no zuo no die"

2. 异常的两种处理方式

标准格式:try...catch...finally
  变形格式
     1try{
       		 //可能出现问题的代码
        }catch(异常类名 变量名){
        	 //处理异常
        }
                       
     2try{      
             //可能出现问题的代码
        }catch(异常类名 变量名){
             //处理异常1
        }catch(异常类名 变量名){
             //处理异常2
        }
        
     注意:trt...catch...catch...catch...不能将大的异常放在最前面
     
        //多线程:jdk5以后:Lock:接口 (锁:可重复入的互斥锁)
     3try{
 			 //可能出现问题的代码
        }finally{
             //释放资源(系统资源)
        }
     4)try{(jdk7以后提供的)
             //可能出现问题的代码
        }catch(异常类名1 | 异常类名2 | 异常类名3  ...变量名){  //异常类名必须为同级别
            处理异常
        }
throws:抛出

:

public class ExceptionDemo1 {
    public static void main(String[] args) {
        //定义两个变量
      /*  int a = 10 ;
        int b = 0 ;
        System.out.println(a/b); //jvm在内存中会创建当前异常类的对象  new ArithmeticException()*/
      //使用try...catch进行捕获异常
        try{
            //可能出现问题代码
            int a = 10 ;
            int b = 0 ;  //直接获取到的,以后可能值---->通过一些方法获取到的值
            System.out.println(a/b);
            System.out.println("over");
        }catch(ArithmeticException e){  //捕获异常:可以使用大的Exception,但是捕获:具体异常具体捕获
            System.out.println("除数不能为0");
        }
        //....
    }
}


//使用try...catch去处理多个异常

public class ExceptionDemo2 {
    public static void main(String[] args) {
        //method1() ;
        //method2() ;
        //method3() ;
        method4() ;
    }
    /*
        jdk7以后提供的 try{
                    可能出现问题的代码
        }catch(异常类名1 | 异常类名2 | 异常类名3  ...变量名){  //异常类名必须为同级别
            处理异常
        }
     */
    private static void method4() {
        try {
            int a = 10;
            int b = 0;
            int[] arr = {4, 5, 6};
            System.out.println(a / b);
            System.out.println(arr[0]);
            System.out.println(arr[3]);
        }  catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {//包含了所有 编译时期/运行时期异常
                  System.out.println("程序出问题了...");
        }
    }

    
//使用trt...catch...catch...catch...不能将大的异常放在最前面
    
    private static void method3() {
        try {
            int a = 10;
            int b = 1; //可能为0
            int[] arr = {4, 5, 6};
            arr = null; //空值
            System.out.println(a / b);
            System.out.println(arr[0]);
            System.out.println(arr[3]);
        }  catch (Exception e) {//包含了所有 编译时期/运行时期异常
            System.out.println("程序出问题了...");
        }
       /* }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("访问了数组中不存在的索引");
        }catch(ArithmeticException e){// 通过jvm创建当前异常类对象,如果当前e类型匹配就会catch语句
            System.out.println("除数不能为0");
        }*/
    }

//针对多个异常进统一处理,try...catch...catch
    
    private static void method2() {
        try{
            int a = 10;
            int b = 1 ; //可能为0
            int[] arr = {4,5,6} ;
            arr = null ; //空值
            System.out.println(a/b);
            System.out.println(arr[0]);
            System.out.println(arr[3]);
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("访问了数组中不存在的索引");
        }catch(ArithmeticException e){// 通过jvm创建当前异常类对象,如果当前e类型匹配就会catch语句
            System.out.println("除数不能为0");
        }catch (Exception e){
            System.out.println("程序出问题了...");
        }
    }

//分别针对可能出现问题的代码,进行try...catch(不推荐)
    
    private static void method1() {
        try{
            int a = 10;
            int b = 0 ;
            System.out.println(a/b);
        }catch (ArithmeticException e){
            System.out.println("除数不能为0");
        }
        //创建一个数组
        try{
            int[] arr = {4,5,6} ;
            System.out.println(arr[0]);
            System.out.println(arr[3]);
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("访问了数组中不存在索引");
        }
    }
}

3. 编译时期异常和运行时期异常

编译时期异常和运行时期异常的区别?
RuntimeException:运行时期异常
            很多的子类:NullPointerException,ClassCastException,ArrayIndexOutOfBoundsException....
运行时期异常:
            一般程序员逻辑结构不严谨导致的问题,调用者可以进行显示处理(try...catch.../throws)
            也可以不进行显示处理,通过逻辑语句进行处理!
                
编译时期异常:调用者必须显示处理,不处理,编译通过不了,程序运行不了
  			如果在当前方法中已经去捕获了try...catch...,调用者无序进行处理,
            但是如果在方法中抛出异常的,调用者必须处理(捕获/抛出throws)
            
开发中:     尽量优先使用try...catch异常,其次再是throws


public class ExceptionDemo3 {
    public static void main(String[] args) throws Exception {
        method1() ;//调用者
       /* try {
            method2() ;
        } catch (ParseException e) {
            System.out.println("解析出问题了...");
        }*/
       method2();
    }
    
    //编译时期异常
    private static void method2() throws ParseException,ClassNotFoundException,Exception {
        //String日期文本--->java.util.Date日期对象(解析)
        //捕获异常.调用者不会进行处理
   /*     try{
            String s = "2021-8-3" ;
            //创建SimpleDateFormat对象
            SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd") ;
            Date dat =  sdf.parse(s) ; //parse方法本身就有一个异常,throws ParseException
        }catch (ParseException e){
            System.out.println("解析出问题了...");
        }*/
    //在当前方法中使用throws
        String s = "2021-8-3" ;
        //创建SimpleDateFormat对象
        SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd ") ;
        Date dat =  sdf.parse(s) ;
    }

    //运行时期异常
    private static void method1() {
        //显示处理了 (能使用异常处理方式:捕获/抛出throws,就是用他)
       /* try{
            int a = 10 ;
            int b = 0 ;
            System.out.println(a/b);
        }catch (Exception e){
            System.out.println("除数为0了...");
        }*/
       //并没有做显示处理
       int a = 10 ;
       int b = 0 ;
       if(b!=0){
           System.out.println(a/b);
       }else{
           System.out.println("除数为0了...");
       }
    }
}

4. 注意

1)有的时候没有办法去抛出,继承关系中,如果子类继承父类,
     父类的该方法没有异常,子类重写该方法的时候,只能try...catch
2)子类继承父类,如果父类的该方法本身抛出异常了,那么子类重写该方法的时候,要么跟父类的方法的异常类名一致,要么是该异常的类子类!
public class ExceptionDemo4 {
    public static void main(String[] args) {
    }
}
class Father{
    public void show(){}
    public void method() throws  ArrayIndexOutOfBoundsException{}
}

class Son extends  Father{
   /* @Override
    public void method() throws Exception { //要么跟他父类的方法异常类名一致,要么是异常类名的子类
    }*/
    @Override
    public void method() throws ArrayIndexOutOfBoundsException {//要么跟他父类的方法异常类名一致,要么是异常类名的子类
    }
    public void show()  {
        //将String--->Date
        try{
            String source = "2021-8-3" ;
            //创建SimpleDateFormat对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
            //解析
            Date date = sdf.parse(source) ;
            System.out.println(date);
        }catch (ParseException e){
            System.out.println("解析出问题了...");
        }
    }
}

5. Throw 和 Throws

面试题    
throws和throw的区别?
       共同点:都是抛出
       用法不同:
 1)使用位置不同
           throws:
                  a)将异常抛出在方法声明上
                  b)在方法名的后面可以跟多个异常类名,中间逗号隔开!
           throw
                  a)在方法的语句体中某个逻辑语句中
                  b)它后面只能跟异常对象,而不是类名
  2)调用者是否处理不同
           throws: 调用者必须进行显示处理(try...catch/throws),否则报错
           
           throw: 调用者无须显示处理,一般情况都是在逻辑语句进行处理
  3)出现异常是否肯定性
           throws:在方法上的,执行某个方法的代码中,可能有问题(表示出现异常的一种可能性)
           
           throw:执行某段代码一定会执行这个异常(表示出现异常的一种肯定性)
  4)
           throws---->将具体的处理交给jvm---通过jvm吧异常信息打印控制台上
                       显示的底层源码而且会显示当前错误消息字符串
           throw---->程序 中某段代码有问题:只是打印异常类名(jvm处理)
public class ExceptionDemo5 {
    public static void main(String[] args) throws ParseException {
//        try {
//            method();
//        } catch (ParseException e) {
//            System.out.println("解析问题了...");
//        }
        method();
      //  method2();
    }
    
    //throw
    private static void method2() throws ArithmeticException {
        int a = 10 ;
        int b = 0 ;
        //逻辑语句
        if(b!=0){
            System.out.println(a/b);
        }else{
            //抛出一个对象
            //throw 匿名对象 new XXXException() ;
            throw new ArithmeticException() ;
        }
    }

    //throws
    private static void method() throws ParseException {
        //将String--->Date
        String source = "2021-8-3" ;
        //创建SimpleDateFormat对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
        //解析
        Date date = sdf.parse(source) ;
        System.out.println(date);
    }
}

6. 异常处理方法

不管使用throws/try...catch...finally:
      都是要通过jvm调用Throwable里面的功能完成日志(错误信息)打印
          try{
               //可能出现问题的代码   //首先加载这些代码 (语法校验)
           }catch(异常类名 变量名){       //如果存在问题,那么jvm在内存中创建异常的实例,实例是否为catch语句中类型的实例
                                                  //类似于  a instanceOf 引用类型:如果是就会执行catch语句
               异常处理                            //要么手动处理/要么通过jvm进行处理(打印错误日志信息)
           }finally{
               释放资源...
           }
 Exception----->Throwable
      1)public String getMessage()获取详细消息字符串   同义的方法:public String getLocalizedMessage()
      2)public String toString():打印出当前异常信息的简短描述:
               内存中异常对象的类名:(全限定名称:包名.类名) 换行
               “:”(一个冒号和一个空格)  换行
               public String getMessage():消息字符串
      3)public void printStackTrace():跟踪堆栈:包含toString以及底层原码(某行代码出现问题)以及本类中的代码问题
               都是体现方法上(将当前跟方法相关的底层方法全部跟踪一遍)
finally:不能单独使用,它是结合try...catch....finally:异常的标准格式
        finally用法:
                    特点:
                         释放相关的资源,代码一定执行的
                         除非在执行finally,jvm退出了!


public class ExceptionDemo6 {
    public static void main(String[] args) {
        try{
            String source = "2021-8-3" ;
            //创建SimpleDateFormat对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
            //解析
            Date date = sdf.parse(source) ;
            System.out.println(date);
        }catch (ParseException e){
            //使用Throwabl里面的功能
            //String result = e.getMessage();//获取消息字符串
           // System.out.println(result);
           // String result = e.toString();
           // System.out.println(result);
            e.printStackTrace();
            System.exit(0);
        }finally{
            //finally语句代码是一定执行的!
            //释放资源
            System.out.println("需要释放当前系统资源");  //jdbc(  Connection连接对象/执行对象Statement)
                                                        //IO流(创建流对象,流对象得释放----系统资源)
            //框架里面:Mybatis(JDBC的封装): 执行对象SqlSession
        }
        //快捷键:针对try---catch---finally: alt+ctrl+t--->try...catch:捕获
    }
}

7. 面试题

如果在某个方法中捕获异常,但是该方法有返回值类型,如果在catch语句出现return语句,finally代码还会执行吗?
 如果会执行,return前还是在后

 答:finally会执行,但是现在这个代码,catch语句已经形成返回路径,它会记录最终返回就是30;finally是去释放资源用的,很少牵扯业务代码;都会执行的,除非jvm退出!
     
public class Test {
    public static void main(String[] args) {
        int num = getNum(10) ;// i=10
        System.out.println(num);
    }
    private static int getNum(int i) {
        try{
            i = 20 ; //i = 20 ;
            System.out.println(i/0); //除数为0
        }catch (ArithmeticException e){
            i = 30 ;        //i =30
            return i ;      // return i  = return 30 :已经在catch语句形成返回的路径 返回结果就是30
        }finally {          //finally代码一定会执行,除非jvm退出了
            i = 40 ;        // i = 40
        }
       return i;       //30
    }
}

二十二. 线程

1.线程

线程是依赖于进程的
  进程:
        能够调用的系统资源的独立单位!
        理解:计算机---->打开任务管理器---->客户端软件---->应用进程
                                           计算机开启启动 ---->服务进程(后台进程)
        现在的计算机----"多进程计算机"  意义?
             主要为了提高CPU的使用率,
	    在玩游戏的同时,还可以听音乐----->开启游戏的进程,音乐软件的进程 是同时的吗?
 			 不是同时,在一点点(时间片),在两个进程进行高效切换!
  线程 :
        属于 程序中执行的最小单元(进程中的一条任务线)
        一个进程有多个线程组成,多个线程----->线程组(ThreadGroup)
        一个线程看成是某个任务,线程的执行具有随机性(多个线程并发执行)以及原子性
  多线程的意义?
          多线程的特点:具有随机性
          多个线程在抢占CPU的执行权
                举例:
                        1v3 打篮球,只能3个人抢占篮球的几率大,并不一定这个3个人就一直能够抢占篮球
                        有可能1个人抢占的篮球几率大(线程的执行具有随机性)
检验多线程安全问题的标准:
     1)是否是多线程环境  
     2)是否存在共享数据 (必须要有共享数据)
     3)是否存在多条语句对共享数据的操作

面试题:

jvm是多线程吗?
    是多线程:
          至少有两条线程
          用户线程main,以及创建对象的时候,当对象使用完毕,需要被垃圾回收器回收;
          jvm针对没有更多引用对象,开启一条垃圾回收线程!
java语言能够开启多线程?
    开启线程---->开启进程----Java语言不能够开启进程---借助于底层语言C语言开启进程
    封装成本地方法 ----->jdk提供类:Thread 里面封装好的方法
            开启线程:start()

2. Thread 类(线程的实现方式一)

线程模拟线程环境
       开启两条线程
创建线程的实现 方式1:
     1)将一个类声明为Thread的子类
     2) 这个子类应该重写Thread类的run方法
     3)然后可以分配并启动子类的实例。
            start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,
            
            run()只是一个普通方法,不会出现线程的执行具有随机性(不会互相抢占cpu执行权)
            
            只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新的线程
                        不会出现两个线程并发执行
public class ThreadDemo {
    public static void main(String[] args) {
        //3)创建Thread类的子类对象
        MyThread my1 = new MyThread() ;//第一个线程对象
        MyThread my2 = new MyThread() ; //第二个线程对象
        //4)启动
        //my1.run();
        //my2.run();
     /*   my1.start();
        my1.start();
            my1只是代表一个线程对象
            my1将start方法调用两次---->IllegalThreadStateException:非法线程状态异常
            start()原码: 校验当前线程状态:如果线程已经启动了,就不能再启动
           */
     my1.start();//start():有jvm调用底层run方法,出现并发执行
     my2.start();
    }
}

//线程类
public class MyThread extends Thread {
    //重写Thread类的方法
    @Override
    public void run() {
        //run方法里面:一般情况耗时的操作
        for(int x = 0 ; x < 200 ; x ++){
            System.out.println(x);
        }
    }
}

3. Thread类的方法

Thread类的构造方法:
       Thread(String name):创建线程类对象,设置名称
           
Thread类的成员方法
       1public final String getName():获取线程名称
       2public final void setName(String name):设置线程名称
       3)线程的优先级
           a)Thread类中静态常量字段(成员变量field)
           b)public static final int MAX_PRIORITY 10     最大优先级
           c)public static final int MIN_PRIORITY 1      最小优先级
           d)public static final int NORM_PRIORITY 5     默认优先级
           e)public final void setPriority(int newPriority):设置线程的优先级
           f)public final int getPriority():获取优先级
           优先级越大的:抢占CPU的执行权越大
           优先级小的:抢占CPU的执行权越小
           默认优先级:随机性大一些
public class ThreadDemo2 {
    public static void main(String[] args) {//用户线程
        //创建MyThread2类的对象
        MyThread2 t1 = new MyThread2()  ;
        MyThread2 t2 = new MyThread2()  ;
        MyThread2 t3 = new MyThread2()  ;
        // public final void setName(String name):设置线程名称
        t1.setName("洪学佳") ;
        t2.setName("张俊杰") ;
        t3.setName("高圆圆");
        t1.setPriority(10); //最大优先级
        t2.setPriority(1);//最小优先级
        int num1 = t1.getPriority();
        int num2 = t2.getPriority();
        int num3 = t3.getPriority();
        System.out.println(num1+"---"+num2+"---"+num3);
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

public class MyThread2 extends Thread {
    //t1,t2
    @Override
    public void run() {
        for(int x = 0 ; x < 100 ; x ++){
            //获取线程名称
            System.out.println(this.getName()+":"+x);
        }
    }
}

4. 方法

1public final void join() throws InterruptedException    ://等待该线程终止!
2public static void yield():        //暂停当前正在执行的线程,执行对方线程
3public final void setDaemon(boolean on)     //守护线程

5. join

public final void join() throws InterruptedException:等待该线程终止!
       底层依赖于线程安全的方法join(0):永远等待(当前执行完毕结束!)
                               又依赖于wait(long time)
       考虑安全问题:
                     三个线程---优先级 都是默认(5)
                     都去设置join(): 三个线程谁先抢占到谁先执行
public class ThreadJoinDemo {
    public static void main(String[] args) {
        //创建三个线程
        JoinThread jt1 = new JoinThread() ;
        JoinThread jt2 = new JoinThread() ;
        JoinThread jt3 = new JoinThread() ;
        //设置线程名称
        jt1.setName("李渊") ;
        jt2.setName("李世民") ;
        jt3.setName("李元霸") ;
        //启动线程
        jt1.start();
        //jt1调用join
        try {
            jt1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        jt3.start();
    }
}


public class JoinThread  extends Thread{
    @Override
    public void run() {
        for(int x =0  ; x < 100 ; x ++){
            System.out.println(getName()+":"+x);
        }
    }
}

6. yield

public static void yield()://暂停当前正在执行的线程,执行对方线程
public class ThreadYieldDemo {
    public static void main(String[] args) {
        //创建两条线程对象
        YieldThread yt1 = new YieldThread() ;
        YieldThread yt2 = new YieldThread() ;
        //设置名称
        yt1.setName("高圆圆") ;
        yt2.setName("赵又廷") ;
        //启动线程
        yt1.start();
        yt2.start();
    }
}

public class YieldThread  extends Thread{
    //yt1/yt2
    @Override
    public void run() {
        for(int x = 0 ; x <100 ; x ++){
            System.out.println(getName()+":"+x);
            Thread.yield(); //暂停当前线程,执行对方线程
        }
    }
}

7.setdaemon

public final void setDaemon(boolean on)
    参数为true,表示标记当前线程为守护线程,当正在运行的线程如果都是守护线程,则jvm自动退出
    这个方法必须在启动线程之前调用(start()之前)
举例:
    玩游戏:坦克大战
    这个两个坦克----守护线程
如果运行的线程都是守护线程,jvm退出,运行的线程不会立即停止掉!
public class ThreadDaemonDemo {
    public static void main(String[] args) {

        //创建两个线程
        ThreadDaemon td1 = new ThreadDaemon() ;
        ThreadDaemon td2 = new ThreadDaemon() ;
        //设置名称
        td1.setName("张飞");
        td2.setName("关羽");
        //设置为守护线程
        td1.setDaemon(true) ;
        td2.setDaemon(true) ;
        //启动线程
        td1.start();
        td2.start();
      //public static Thread currentThread():获取正在运行的线程执行对象的引用
        Thread.currentThread().setName("刘备");
        //提供for循环:
        for(int x = 0 ; x < 5 ; x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}

8. 电影院售票

电影院有三个窗口,共同出售100张票,使用多线程创建方式1来进行实现!
  分析
       1)自定义类SellTicket extends Thread
               tickets:= 100;
       2)重写run方法
                  耗时的操作 :模拟一致有票,使用while(true)
       3)在main用户(主线程)创建三个     sellTicket  对象
       4)分别设置线程名称:窗口1,窗口2,窗口3
       5)分别启动线程
  方式1 多线程的创建方式存在弊端 (会出现重票)
       1)它是一个继承关系, 具有"局限性"
       2)不能够体现资源共享的概念-----      因为Thread类使用到的静态代理 (设计模式)
              st1,st2,st3 :三个栈内存变量
              分别需要new对象
      线程的创建方式 第二种方式优于第一种
public class SellTicketTest {
    public static void main(String[] args) {
        //创建三个窗口
        SellTicket st1 = new SellTicket() ;
        SellTicket st2 = new SellTicket() ;
        SellTicket st3 = new SellTicket() ;
        //设置线程名称
        st1.setName("窗口1");
        st2.setName("窗口2");
        st3.setName("窗口3");
        //启动线程
        st1.start() ;
        st2.start() ;
        st3.start() ;
    }
}

public class SellTicket extends Thread {
    //需要保证票被共用:使用static修饰
    public static int tickets = 100 ;
    //重写run方法
    //st1,st2,st3
    @Override
    public void run() {
        //模拟一直有票
        while(true){
            //模拟网络延迟 :睡眠的过程
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(tickets>0){ //100>0
                System.out.println(getName()+"正在出售第"+(tickets--)+"张票");
            }
            /**
             * 出现同票的原因:存在: 线程的执行  原子性操作 (++,-- :简单,最直接的操作)/延迟性操作
             * st1(窗口1)---->ticket 记录100    正在第100张票
             * st3(窗口3)---->窗口1准备执行ticket--(100-1=99),抢占到执行权了,正在出售100张票
             */
        }
    }
}

9.Runnable(线程的实现方式二)

1)自定义类实现Runnable接口
    2)重写Runnable接口的run方法
    3)在main用户线程中
           可以分配类的实例(创建类的实例)
    4)创建当前类对象,然后创建Thread类对象,将当前类对象作为参数来传递
           当前类---->"资源共享类"
           Thread(Runnable target, String name)
    5)分别启动线程即可!
public class ThreadDemo {
    public static void main(String[] args) {
        //可以分配类的实例(创建类的实例)
        MyRunnable my  = new MyRunnable() ; //资源类:被多线程共享//具体类new 具体类
        //创建两个线程类对象
        Thread t1  = new Thread(my,"张俊杰") ;
        Thread t2  = new Thread(my,"高圆圆") ;
        //分别启动线程
        t1.start();
        t2.start();
    }
}

//资源类
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        //耗时的操作
        for(int  x = 0 ; x < 100 ; x ++){
            //public static Thread currentThread()
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}

第二种实现方式:
    静态代理
         特点:真实角色和代理角色必须实现同一个接口
         真实角色:专注于自己的功能
         代理角色:完成对真实角色功能的"增强"
/*
结婚:
    结婚这个这个情况
        真实角色:You  你自己
        代理角色:WeddingCompany 婚庆公司
*/

public class ThreadDemo {
    public static void main(String[] args) {
        //接口多态
        //Mary mary = new You() ;
        You mary = new You() ;
        mary.mary();
        System.out.println("----------------------");
        //静态代理:通过婚庆公司帮助自己You来完成结婚
        //真实角色
        You you2  = new You() ;     // MyRunnable
        WeddingCompany wc = new WeddingCompany(you2) ;// Thread类对象
        wc.mary();
    }
}

//定义一个接口的接口
interface  Mary{
    void mary() ;//结婚
}

//自己:真实角色
class You implements  Mary{

    @Override
    public void mary() {
        System.out.println("结婚了,很开心...");
    }
}
//代理角色:婚庆公司 在你结婚之前,它可以给你布置婚礼线程, 结婚之后,开开心心吃席
class WeddingCompany implements Mary{
    //将真实角色作为参数传递
    private You you ;
    public WeddingCompany(You you){
        this.you = you ;
    }
    @Override
    public void mary() {
        System.out.println("给你布置婚礼现场...");
        you.mary();  //只要专注于自己的事情!
        System.out.println("婚礼线程布置完毕,吃席...");
    }
}

10. synchronized

1.同步代码块
synchronized(锁对象){
       多条语句对共享数据的操作
}
锁对象:必须为多个线程的同一把锁;锁对象可以是任意Java的类对象
 
面试题:
    wait()方法/notify()方法    ---- 也可以称为"同步"  --->等待唤醒机制
         线程等待/线程唤醒 这些方法为什么不定义在Thread类中呢,而是定义Object类中呢?
         它和锁对象有关系,而锁对象:可以任何的java类对象,-----Object(顶层父类)
         syncronized(锁对象){
             锁对象.wait()
             //锁对象.notify()
         }

电影院卖票---100张票,三个窗口同时出售,
    第二种方式进行实现,更能体现"资源共享"
    
模拟真实场景:网络延迟,加入了睡眠操作
    1)可能出现一张票被卖多次(同票)
    2)可能出现负票(-1)
 
存在安全问题:
    窗口1正在出售第23张
    窗口3睡醒了之后,抢占到了 出售第22张...  如果出现第23张
    
    出现负票:  线程的执行具有随机性, ---加入延迟!
 		 窗口1正在出售第1张票,延迟睡眠150秒 t3醒来之后
         窗口3正在出售第0张票,   --
         t2也在某一刻同时醒来并执行语句---- 窗口2正在出售第-1张票
 
检验多线程安全问题的标准;
     1)是否是多线程环境   是              不能更改,使用多线程实现
     2)是否存在共享数据  是   (资源类的数据: tickets 票)         必须要有共享数据
     3)是否存在多条语句对共享数据的操作 是            解决点
 
解决----Java提供同步机制:同步代码块 将多条对共享数据包裹起来
            synchronized(锁对象){
   				将多条对共享数据包裹起来
            }
 
锁对象:必须要多个线程使用的同一个锁对象,而不是分别自己的锁对象!
              (生活中:火车上上厕所---> 当一个人进去之后,门一关,其他人进不来..)

public class SellTicket implements Runnable {
    //成员变量;100张票
    public static int tickests = 100 ;
    //创建一个锁对象:
    //public Object obj = new Object() ;
    //创建Demo类对
    Demo d = new Demo() ;
    //t1,t2,t3
    @Override
    public void run() {
        //模拟一直票
        while(true){
            //t1先抢占到CPU执行权
            //t1,t2,t3在抢占CPU执行权
            //同步代码块
           synchronized (d){  //t1进来之后,别的t2,t3线程进不来的
                            //t3进来,t1,t2进不来
                if(tickests>0){//100>0
                    try {
                        Thread.sleep(100); //单位为毫秒数
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
                }
            }//t1出来,t3出来---回到同步代码块之前,继续抢
            //锁的释放,同步结束之后就会释放锁(同步锁)
        }

    }
}
class Demo{}

public class SellTicketTest {

    public static void main(String[] args) {
        //创建资源类对象SellTicket
        SellTicket st = new SellTicket() ;
        //创建三个线程类对象
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;
        //分别启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

public class SellTicketTest {
    public static void main(String[] args) {
        //创建资源类对象SellTicket
        SellTicket st = new SellTicket() ;
        //创建三个线程类对象
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;
        //分别启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

public class SellTicket implements Runnable {
    //成员变量;100张票
    public static int tickests = 100 ;
    //创建一个锁对象:
    public Object obj = new Object() ;
    //t1,t2,t3
    @Override
    public void run() {
        //模拟一直票
        while(true){
            //t1,t2,t3
            //解决方案:
            //将多条语句对共享数据的操作包裹起来
            //synchronized (new Object()){  //锁对象 :三个线程分别使用自己的锁
                //必须为是同一个锁对象
           synchronized (obj){
                //模拟网络延迟
                //判断
                if(tickests>0){//100>0
                    //t1先进来,睡眠150毫秒,t1已经睡完了,执行下面的操作
                    //t3先进来,睡眠150毫秒,t3醒来之后
                    //t2最后抢占到,醒来之后
                    try {
                        Thread.sleep(100); //单位为毫秒数
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //输出窗口信息
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
                }
            }
            /**
             * 出现同票:  线程的原子性操作(++,--:最简单的操作)
             * 原子性:记录原始数据值,然后再对数据自增或者自减
             *  t1执行的操作,  窗口1正在出售第 23张票 ,当准备--的时候,t3将t1里面tickets--还没执行的时候,就已经打印了
             *                窗口3正在出售第23张票
             *
             *         t2进来之后,tickets-- 动作完成了  22张
             *          窗口2正在出售第22张票
             *
             *   出现负票:  线程的执行具有随机性, ---加入延迟!
             *
             *          窗口1正在出售第1张票,延迟睡眠150秒 t3醒来之后
             *          窗口3正在出售第0张票,   --
             *          t2也在某一刻同时醒来并执行语句---- 窗口2正在出售第-1张票
             *
             */
        }
    }
}

2. 同步方法
什么是同步方法? 如果一个方法的方法体的第一句话就是同步代码块
    可以将synchronized关键字提取到方法声明上,跟在权限修饰符的后面
    权限修饰符 synchronized 返回值类型 方法名(形式列表){   //非静态的同步方法
        业务逻辑...
    }
锁作用于方法:
   锁对象是什么?  默认都是非静态的同步锁的是this:当前类对象的地址值引用
 			    静态的同步方法:锁对象,跟类相关: 当前类的字节码文件对象:  类名.class
public class SellTicket implements Runnable {
    //定义100张票
    public static int tickets = 100 ;
    //定义一个统计变量
    int x = 0 ;
    public Object obj = new Object() ;//锁对象obj
    @Override
    public void run() {
        while(true){
            if(x % 2 ==0){  //
               // synchronized (obj){
//                synchronized (this){ //跟下面的非静态同步方法锁对象一致
                  synchronized (SellTicket.class){ //跟下面的静态的同步方法锁对象一致
                    if(tickets>0){
                        //睡眠
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
                    }
                }
            }else{
                sellTicket() ; //同步方法
            }
            x ++ ; //x=1
        }
    }
        //调用了一个卖票 方法
        // sellTicket() ;
    //同步方法
//    public synchronized void sellTicket() {//sellTicket 的锁对象是什么?  默认都是非静态的同步锁的是this:当前类对象的地址值引用
    public static synchronized void sellTicket() {  //静态的同步方法:锁对象,跟类相关: 当前类的字节码文件对象:  类名.class
            if(tickets>0){
                //睡眠
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        //创建资源类对象
        SellTicket st = new SellTicket() ;
        //创建线程类对象 将st作为参数传递
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

11. 优化

1. 死锁
线程安全问题:可以通过同步方法或者是同步代码块去解决,但是执行过程中就可能出现死锁问题
死锁问题:
      (使用同步机制解决线程安全) 线程和线程之间出现了互相等待的情况!
      解决方案:
             多个线程之间的通信:必须使用的是一个资源类对象,而不能是每一个线程在使用自己的资源类对象!
             使用生成者和消费者模式思想去解决,前提条件:生成者线程和消费者线程 必须操作的同一个资源类对象!
//死循环

//锁对象
public class MyMonitor {
    //提供两把锁对象
    public static final Object objA = new Object() ;
    public static final Object objB = new Object() ;
}
//资源类
public class DieLock implements Runnable {
    private boolean flag  ;//标记值
    public DieLock(boolean flag){
        this.flag = flag ;
    }
    @Override
    public void run() {
        //判断标记值
        //t1 ---->DieLock(true)
        //t2 ---->DieLock(false)
        if(flag){
            //t1
            synchronized (MyMonitor.objA){
                System.out.println("if ObjeA");//"if objA"
                synchronized (MyMonitor.objB){
                	System.out.println("if objB");// "if objB"
                }
            }
        }else{
            //t2
            synchronized (MyMonitor.objB){
                System.out.println("else ObjB"); //else objB
                synchronized (MyMonitor.objA){
                    System.out.println("else objA");  //"else objA"
                }
            }
        }
        /**
         * t2线程先抢到了
         * else ObjB        ---->等待ObjA锁释放
         * if ObjeA         ---->等待ObjB锁释放
         *
         * t1线程先抢占到了
         * if ObjeA
         * else ObjB
         */
    }
}

2. 优化1
/**
 * @author Kuke
 * @date 2021/8/5
 *  使用生成者和消费者思想模式---解决线程死锁问题
 *  1)StuffBun包子类属性
 *          包含包子的名称name
 *          包子的大小type
 *  2)生产者资源类      SetBun   产生包子
 *  3)消费者资源类      GetBun   使用包子
 *  4)ThreadDemo:main 用户线程
 *  按照上面的方式:模拟生产者产生数据,消费者使用数据出现问题 null---null
 *  生产资源类中和消费者资源类中所操作的包子对象不是同一个对象!
 *  可以将包子通过生产资源类或者消费者资源类 通过构造方法传递
 *  优化1:
 *          加入while循环,模拟包子一直生产和一直消费!
 *          出现问题:数据紊乱:加入同步代码块给每一个资源类中都加入解决! 将多条语句对共享数据的操作包起来!
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //创建一个包子对象
        StuffBun sbu  = new StuffBun() ; //同一个对象
        //创建生产资源类对象
        SetBun sb = new SetBun(sbu) ;
        //消费者资源类对象
        GetBun gb = new GetBun(sbu) ;
        //创建线程了对象
        Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
        Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
        t1.start();
        t2.start();
    }
}

//包子类
public class StuffBun {
    //成员变量不私有化
    String name ;//包子的类型(肉包子,菜包子)
    String bunType ;//大包子/小包子
}

//生产者资源类(做包子)
public class SetBun implements  Runnable {
    //声明这个包子类
    private StuffBun stu ;
    public SetBun(StuffBun stu){
        this.stu = stu ;
    }
    //定义一个统计变量
    int x = 0 ;
    @Override
    public void run() {
        //产生包子
      /*  StuffBun stu = new StuffBun() ;
        stu.name = "肉包子" ;
        stu.bunType = "大类型";*/
      //不断的产生数据
      while(true){
          synchronized (stu){
              if(x % 2 == 0){//t1
                  stu.name = "肉包子" ;
                  stu.bunType = "大包子";
              }else{
                  stu.name = "菜包子" ;
                  stu.bunType = "小包子" ;
              }
          }
          x ++ ;
      }
    }
}

//消费者资源类(吃包子)
public class GetBun implements Runnable {
    //声明包子类的变量stb
    private StuffBun stb ;
    public GetBun( StuffBun stb){
        this.stb = stb ;
    }
    @Override
    public void run() {
        //模拟要使用数据
      //  StuffBun stb = new StuffBun() ;
        //不断使用数据
        while(true){
            synchronized (stb){
                System.out.println(stb.name+"---"+stb.bunType);
            }
        }
    }
}

3. 优化2 wait ()+notify()
使用生成者和消费者思想模式---解决线程死锁问题
    1)StuffBun包子类属性
           包含包子的名称name
           包子的大小type
    2)生产者资源类      SetBun   产生包子
    3)消费者资源类      GetBun   使用包子
    4)ThreadDemo:main 用户线程
优化2:
     想出现依次打印
          肉包子---大包子
          菜包子---小包子
                   ...
wait()+notify()--->实现同步机制(并且同时信号法:将死锁问题解决!)

//包子类
public class StuffBun {
    //成员变量不私有化
    String name ;//包子的类型(肉包子,菜包子)
    String bunType ;//大包子/小包子
    //定义标记:表示是否存在包子数据
    boolean flag ; //默认false,没有数据
}


//消费者资源类
public class GetBun implements Runnable {
    //声明包子类的变量stb
    private StuffBun stu ;
    public GetBun( StuffBun stu){
        this.stu = stu ;
    }
    @Override
    public void run() {
        //模拟要使用数据
      //  StuffBun stb = new StuffBun() ;
        //不断使用数据
        while(true){
            synchronized (stu){
                //如果当前消费资源类中存在包子数据,先等待消费使用完毕数据
                if(!stu.flag){
                    //等待使用完毕数据
                    try {
                        stu.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(stu.name+"---"+stu.bunType);
                //改变信号值
                //如果包子消费完毕
                stu.flag = false ;
                //唤醒对方线程(生产者资源类,别等了,产生数据)
                stu.notify();
            }
        }
    }
}

//生成者资源类
public class SetBun implements  Runnable {
    //声明这个包子类
    private StuffBun stu ;
    public SetBun(StuffBun stu){
        this.stu = stu ;
    }
    //定义一个统计变量
    int x = 0 ;
    @Override
    public void run() {
        //产生包子
      /*  StuffBun stu = new StuffBun() ;
        stu.name = "肉包子" ;
        stu.bunType = "大类型";*/
      //不断的产生数据
      while(true){
          synchronized (stu){
              //如果当前生产者没有语句,需要等待生成产生数据
              if(stu.flag){
                  //锁对象调用wait发那个发
                  try {
                      stu.wait();//释放锁对象...
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              if(x % 2 == 0){//t1
                  stu.name = "肉包子" ;
                  stu.bunType = "大包子";
              }else{
                  stu.name = "菜包子" ;
                  stu.bunType = "小包子" ;
              }
              //如果现在有数据了
              //改变信号
              stu.flag  = true ;//有数据类
              //通知(唤醒)消费者线程,赶紧使用数据
              stu.notify(); //唤醒对方线程
          }
          x ++ ;
      }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        //创建一个包子对象
        StuffBun sbu  = new StuffBun() ; //同一个对象
        //创建生产资源类对象
        SetBun sb = new SetBun(sbu) ;
        //消费者资源类对象
        GetBun gb = new GetBun(sbu) ;
        //创建线程了对象
        Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
        Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
        t1.start();
        t2.start()
    }
}
 //包子类
// 所有属性私有修饰
public class StuffBun {
    //成员变量不私有化
   private  String name ;//包子的类型(肉包子,菜包子)
   private String bunType ;//大包子/小包子
    //定义标记:表示是否存在包子数据
    private boolean flag ; //默认false,没有数据
    //提供给包子数据进行赋值的方法
    public synchronized void set(String name,String bunType){ //锁对象是this:非静态的同步方法
            //如果当前生产者没有语句,需要等待生成产生数据
            if(this.flag){
                //锁对象调用wait发那个发
                try {
                    this.wait();//释放锁对象...
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //赋值
            this.name = name ;
            this.bunType = bunType ;
            //如果现在有数据了
            //改变信号
            this.flag  = true ;//有数据类
            //通知(唤醒)消费者线程,赶紧使用数据
            this.notify(); //唤醒对方线程

    }
    //提供方法:获取包子的数据
    public synchronized void get(){ //非静态的同步方法:锁对象 this
            //如果当前消费资源类中存在包子数据,先等待消费使用完毕数据
            if(!this.flag){
                //等待使用完毕数据
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(this.name+"---"+this.bunType);
            //改变信号值
            //如果包子消费完毕
            this.flag = false ;
            //唤醒对方线程(生产者资源类,别等了,产生数据)
            this.notify();
    }
}

// 生成者资源类
public class SetBun implements  Runnable {
    //声明这个包子类
    private StuffBun stu ;
    public SetBun(StuffBun stu){
        this.stu = stu ;
    }
    //定义一个统计变量
    int x = 0 ;
    @Override
    public void run() {
        //产生包子
      /*  StuffBun stu = new StuffBun() ;
        stu.name = "肉包子" ;
        stu.bunType = "大类型";*/
      //不断的产生数据
      while(true){
          if(x % 2 == 0){//t1
              //stu.name = "肉包子" ;
              //stu.bunType = "大包子";
              stu.set("肉包子","大包子");
          }else{
              //stu.name = "菜包子" ;
              //stu.bunType = "小包子" ;
              stu.set("菜包子","小包子");
          }
          x ++ ;
      }
    }
}


//消费者资源类
public class GetBun implements Runnable {
    //声明包子类的变量stb
    private StuffBun stu ;
    public GetBun( StuffBun stu){
        this.stu = stu ;
    }
    @Override
    public void run() {
        //模拟要使用数据
      //  StuffBun stb = new StuffBun() ;
        //不断使用数据
        while(true){
            stu.get();//获取包子数据
        }
    }
}

//测试类
public class ThreadDemo {
    public static void main(String[] args) {
        //创建一个包子对象
        StuffBun sbu  = new StuffBun() ; //同一个对象
        //创建生产资源类对象
        SetBun sb = new SetBun(sbu) ;
        //消费者资源类对象
        GetBun gb = new GetBun(sbu) ;
        //创建线程了对象
        Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
        Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
        t1.start();
        t2.start();
    }
}

12.Lock锁

JDK5以后提供java.util.current.locks.Lock   :提供比syncrhonized方法(/同步代码块)更具体的锁定操作
多个线程并发访问,抢占共享资源数据,通过lock实现多个线程对某个共享资源进行独占访问,不会安全问题!
    Lock是一个接口
    
       void lock()获取锁
       void unlock() 试图释放锁
       提供跟具体的子实现类:
                   ReentrantLock
//资源类---需要被多个线程进行共享
public class SellTicket implements  Runnable {
    //定义100张票
    private static int tickets = 100 ;
    //创建一个锁对象
    Lock lock = new ReentrantLock() ;
    @Override
    public void run() {
        //模拟一只有票
        while(true){
            //通过锁对象--->获取锁
            lock.lock();
            //try...catch...finaly:捕获异常
            //使用try..finally
            try{
                //判断
                if(tickets>0){
                    //睡眠100毫秒
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
                }else{
                    break ;
                }
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }
}

public class LockDemo {
    public static void main(String[] args) {
            //创建共享资源类对象
        SellTicket st = new SellTicket() ;
        //创建三个线程类对象
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

13.线程组(了解)

线程组  ThreadGroup
    线程组代表一组线程。 此外,线程组还可以包括其他线程组
   Thread:---->  public final ThreadGroup getThreadGroup() {//获取线程组
               return group;
        }
          //给线程设置默认的线程组名称
          //public Thread(ThreadGroup group, Runnable target) {
   ThreadGroup:
                public final String getName() {:获取默认的线程组名称
                        return name;
     }
构造方法;
       public ThreadGroup(String name) {}
线程组: 将线程可以都添加一组中,方便管理,
       线程启动完毕之后,线程终止之后,不会将这个线程对象在内存中重复利用
任务队列--------一个任务称为"一个线程" , Quene

public class MyThread implements Runnable {
    @Override
    public void run() {
        for(int x = 0 ;x < 100 ;  x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}

public class ThreadGroupDemo {
    public static void main(String[] args) { //jvm调用main方法
      //  method1();
        method2() ;
    }
    //设置一个新的线程组名称
    private static void method2() {
        //创建一个线程组对象--同时设置线程组名称
        ThreadGroup tg = new ThreadGroup("myMain") ;
        //创建两条线程对象
        MyThread my = new MyThread() ;
        Thread t1 = new Thread(tg,my) ;
        Thread t2 = new Thread(tg,my) ;
        //获取线程组对象并同时线程组名称
        String name1 = t1.getThreadGroup().getName();
        String name2 = t2.getThreadGroup().getName();
        System.out.println(name1+"---"+name2);
    }

    private static void method1() {
        //创建两个线程
        MyThread my  = new MyThread() ;
        Thread t1 = new Thread(my) ;
        Thread t2 = new Thread(my) ;
        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();
        String name1 = tg1.getName();
        String name2 = tg2.getName();
        System.out.println(name1+"---"+name2);  //默认线程组名称都是main
    }
}

14.线程池(重点)

线程池 属于 "池"化技术   ------>相似 数据库连接池(dbcp,c3po,druid(为监控而生))
特点:
    在内存中创建一个固定可重用的线程数,当前线程执行完毕终止了,不会被回收掉,再次回到线程池中,等待下一次利用!
弊端: 维护成本大
ExecutorService---接口
通过 工厂类:
Exceutors
        创建一个固定的可重用的线程数,返回值就线程池对象
        public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService
         提交队列任务(多个线程并发执行)
         <T> Future<T> submit(Callable<T> task)
         提交值返回任务以执行,并返回代表任务待处理结果的FutureFuture<?> submit(Runnable task)
        submit的返回值:异步计算的结果,如果不做计算,无须返回结果!
public class ThreadPoolDemo {
    public static void main(String[] args) {
        //通过工厂类创建线程池对象
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        //提交异步方法
        //MyRunnable:打印x的值0-99之间的数据,不需要返回结果
      //  threadPool.submit(new MyRunnable()) ;
      //  threadPool.submit(new MyRunnable()) ;
        //   <T> Future<T> submit(Callable<T> task)
        //Callable:提交异步计算---需要重写Callable的call来计算结果;如果没有结果,直接在call无须返回
        threadPool.submit(new MyCallable()) ;
        threadPool.submit(new MyCallable()) ;
        //void shutdown()关闭线程池
        threadPool.shutdown();
    }
}

public class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        for(int x = 0 ; x < 100 ; x++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
        return null;
    }
}

public class MyRunnable implements  Runnable {
    @Override
    public void run() {
        for(int x = 0 ; x < 100 ; x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}

15.设计原则

设计原则:
   开闭原则:
            对现有代码修改关闭,对扩展代码开放
            举例:
                    项目开发完毕,进行更新,不能够修改现有代码,在现有代码的基础上提供扩展!
   接口分离原则
           一个接口中定义一个功能,接口和接口之间独立的,不能相互影响
                   实际开发中:
                          某个接口中,可能将相关的功能都定义在这一个接口中
                          按模块划分:
                                   用户模块        UserDao  接口
                                                       login()/register()/logout()/checkUserName()
                                   商品模块
                                                   ProductDao 接口
                                                       Product findById(Integer id) ;
                                                        List<Product> findAll() ;
                                                        void update(Product product);
                                   订单模块
                                               OrderDao 接口
                                                        List<Order> findPage(int pageSize,int currentPage);
   里氏替换原则:任何父类出现的地方都可以子类替代!
               class Father{
                     public void show(){
                        // ...
                        Class<Son> clazz  = Son.class ;
                        //反射方式---->字节码文件对象就调用method----->将所有的成员方法---->Method
                      }
                }
                class Son extends Father{
                      public void method(){
 
                      }
                }
       23种设计模式都需要遵循 原则"低耦合,高内聚"
       设计模式是一种思想,前人总结出来,不是技术!
 
         创建型:对象的创建 (使用最多)
         结构型:整个结构的组成
               代理模式
                   静态代理
                           代理角色
                           真实角色
         行为型:具备功能性的
创建型设计模式:
         简单工厂:----称为 静态工厂方法模式
         优点:利用多态创建子类对象,能够灵活去创建对象(提供的静态功能)
         弊端:代码量大,一旦有新的类型增加,工厂类的静态功能就需要改动...
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值