java程序设计2

零、前言

0.1 Java发展史(了解)

https://zhuanlan.zhihu.com/p/465034540#Java%E5%8F%91%E5%B1%95%E5%8F%B2

0.2 Java运行环境

安装教程

专业术语

jdk(java develope kid)java开发工具

jre(java run environment)java运行环境

jvm (java virtual machine)java虚拟机

JavaSE     java基础版     

JavaEE     java进阶版web模块

JavaME   java进阶版但是过时了 常用于遥控器等电子设备

使用java前的必备配置

package 包(目录,文件夹)
Class      类,Java源代码

open  打开
close 关闭
cancel 取消

root  权限
install  安装
software  软件
VM  虚拟机

https://blog.csdn.net/daiyi666/article/details/116333121

0.3 java开发工具

新手推荐

记事本 (纯手搓)

eclipse (免费、功能基本齐全)

官网

官网 Eclipse Downloads | The Eclipse Foundation

教程

https://blog.csdn.net/qq_45344586/article/details/123942685

掌握基本功

IDEA (资源丰富,符合人体工程学)

安装教程 

https://www.cnblogs.com/bsytz/p/15346827.html

0.4 注释

1、java中的注释的种类:
单行注释、多行注释、文档注释(java特有)

2、单行注释多行注释的作用:
①对程序的代码进行解释说明
②对程序进行调试

3、注意:
①单行注释和多行注释中声明的信息,不参与编译。换句话说,编译以后声明的字节码文件中不包含单行注释和多行注释中的信息
②多行注释不能嵌套使用

4、文档注释:
文档注释内容可以被JDK提供的工具Javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档

javadoc -d mydoc -author comment.java
生成文档注释
// 单行注释

/*
   多行注释
*/

/**
*  文档注释
*  @author
*  @xxx
*/

0.5 标识符

标识符

1、什么是标识符?java中的变量、方法、类等要素命名时使用的字符序列,称为标识符。
技巧:凡是自己可以起的名字的地方都叫标识符。比如:类名、方法名、变量名、包名、常量名等
2、标识符的命名规则(必须遵守!!!否则,编译不通过)
>由26个英文字母大小写,0-9,_或$组成
>数字不可以开头
>不可以使用关键字和保留字,但能包含关键字和保留字。
>java中严格区分大小写,长度无限制。
>标识符不能包含空格

3、标识符命名规范
>包名:多单词组成时所有字母都小写
>类名、接口名:多单词组成时,所有单词的首字母都要大写
>变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始时首字母大写
>常量名:所有字母都大写。多单词时每个单词用下划线连接

说明:大家在定义标识符时,要注意“见名知意”

一、顺序结构

1.1 常量和变量

常量

Java中不可以改变的量就是常量,使用final关键字来定义的常量叫做符号常量

常量也有划分:

整型常量:二进制(0b开头,如 0b123)、八进制(0开头,如 0123)、十进制(阿拉伯数字,如 123)、十六进制(0x开头,如 0x123)

实型常量:单精度常量(数据最后带f或F,如 3.14f)和双精度常量(如 3.14)

字符型常量:使用单引号定界符,如 'b'

逻辑型常量:true 和 false

字符串常量:使用双引号定界符,如 "java"

注意:

① 必须在常量声明时候对其初始化

② final表示最终的,不能再被改变

③ 符号常量通常用大写字母表示

变量

1、变量的理解:内存中的一个储存区域,该区域的数据可以在同一类型的范围内变化不断

2、变量的构成包含三个要素:数据类型、变量名、储存的值

3、java中变量声明的格式:数据类型 变量名 = 变量值;

4、定义变量时,变量名要遵循标识符命令的规则和规范。

5、说明:
①变量都有其作用域。变量只在作用域内是有效的,出了作用域就失效了。
②在同一个作用域内,不能声明两个同名的变量
③定义好变量以后,就可以通过变量名的方式对变量进行调用和运算。
④变量值在赋值时,必须满足变量的数据类型,并且在数据类型有效的范围内变化

1.2 数据类型 

java中的变量按照数据类型来分类:

基本数据类型(8种):

整型:byte,short,int,long

浮点型:float,double

字符型:char

布尔型:boolean

引用数据类型:

字符串 (String)

类(class)

数组(array)

接口(interface)

枚举(enum)

注解(annotation)

记录(record)

1.3 类型转换

1、运算不包含Boolean类型
2、运算规则包括:
   ①自动类型提升
   ②强制类型转换
3、测试自动类型提升
规则:当容量小的变量与容量大的变量做运算时,结果自动转换为容量大的数据类型

   byte-->short-->int-->long-->float-->double

   特别的:byte、short、char类型的变量之间做运算,结果为int类型
说明:此时的容量小或者大,并非指占用的内存空间的大小,而是指表示数据的范围的大小

整型常量,规定是int类型
浮点型常量,规定是double类型
为什么变量不允许数字开头,为了“自洽”

规则:
1、如果需要将容量大的变量的类型转换为容量小的变量的类型,需要使用强制类型转换
2、强制类型转换需要使用强转符:()。在()内指明要转换为的数据类型。
3、强制类型转换过程中,可能导致精度损失。

1.4 运算符和表达式

算术运算符
+ - * / % (前)++ (后)++ (前)-- (后)-- ++
String连接符,String与其他类型的变量间的运算,运算结果也是String类型

基本数据类型与String的运算

关于String的理解
1、String类,属于引用数据类型,俗称字符串。
2、String类型的变量,可以使用一对""的方式进行赋值。
3、String声明的字符串内部,可以包含0个,1个或多个字符。

String与基本数据类型变量间的运算
1、这里的基本数据类型包括Boolean在内的8种。
2、String与基本数据类型变量间只能做连接运算,使用“+”表示。
3、运算的结果是String类型

位运算符

1、<<  >>  >>>  &  |  ^  ~

2、说明:
①<<  >>  >>>  &  |  ^  ~:针对数值类型运算结果也是数值类型
②<<:在一定范围内,每向左移一位,结果就在原有的基础上*2(正负都可以)
③>>:在一定范围内,每向右移一位,结果就在原有的基础上/2(正负都可以)
④&:与,两个1才为1,否则为0
⑤|:或,有1则为1
⑥^:异或,相反为1
⑦~:非,0为1,1为0

赋值运算符

1、=、+=、-=、*=、/=、%=

2、
① 当“=”两侧数据类型不一致时,可以使用自动类型转换或强制类型转换原则进行处理。
② 支持连续赋值
③ +=、-=、*=、/=、%= 操作,不会改变变量本身的数据类型。

1.5 顺序结构编程思想

思路:定义变量、输入、处理、输入

Java的输入输出

输入

Scanner 变量名 = new Scanner(System.in);

如 变量名.nextInt()等,可以输入int、float、double、字符串等数据类型

输入一个英文字母可以使用System.in.read();方法实现

输出

① System.out.println(); 可以换行输出括号里的内容

② System.out.print(); 可以在一行内输出括号里的内容

③ System.out.printf("%s:%",name,age); 格式化输出

二、选择结构

2.1 关系运算符和逻辑运算符

关系运算符

1、==、!=、<、>、<=、>=、instanceof

2、说明
①instanceof 在面向对象多态性中
②==、!=、<、>、<=、>= 适用于除Boolean类型之外的其他7种基本数据类型
运算结果为boolean类型
③==、!= 可以适用于引用数据类型,boolean数据类型
④区分= 和==
逻辑运算符

1、&、&&、|、||、!、^

2、说明
①逻辑运算符针对的都是Boolean类型的变量进行的操作
②逻辑运算符运算的结果也是Boolean类型
③逻辑运算符常使用条件、判断、循环结构中

2.2 条件运算符

1、(条件表达式)?表达式1:表达式2

2、说明

①条件表达式的结果是boolean类型

②如果条件表达式的结果是true,则执行表达式1,如果是false,则执行表达式2

③表达式1和表达式2需要是相同的类型或兼容类型

2.3 运算符优先级(了解)

https://blog.csdn.net/PursueLuo/article/details/108807576

优先级不够时,使用括号()来提升

2.4 if语句

分支结构1:if-else条件判断语句

1、格式
格式1:
if(条件表达式){
   语句块;
}

格式2:
if(条件表达式){
   语句块1;
}else{
   语句块2;
}

格式3:
if(条件表达式1){
   语句块1;
}else if(条件表达式2){
   语句块2
}
...
else if(条件表达式n){
   语句块n;  
}
else{
   语句块n+1;
}

2.5 switch语句

分支结构之Switch-case的使用

1、语法格式

switch(表达式){
   case 常量1:
   执行语句;
   break;
   case 常量2:
   执行语句;
   break;
   ...
   default
   执行语句;
   break;
}

2、执行流程
依次匹配case值,碰到相等的直接执行,break跳出;没有符合条件default执行,break跳出。

3、说明
①switch中的表达式只能式特定的数据类型
byte、short、char、int、枚举(JDK5.0新增)、String(JDK7.0新增)
②case后都是跟的常量,使用表达式与这些常量做相等判断,不能做范围判断
③开发中,使用switch-case时,通常case匹配的情况都是有限的
④break 执行跳出switch-case语句
⑤default 是可选的位置灵活

4、switch-case 与 if-else之间的转换
①可以使用switch-case结构都可以使用if-else
②能使用switch-case尽量使用,效率稍高,穿透性

三、循环结构

3.1 while循环和do...while循环

循环结构之一:while循环

1、凡是循环结构,就一定会有4个要素
①初始化条件
②循环条件  变量或表达式为boolean
③循环体
④迭代部分

2、while的格式
①
while(②){
③
④
}

执行过程:①→②→③→④→②→③→...→②

for和while可以相互转换
循环结构之一:do-while循环

1、凡是循环结构,就一定会有4个要素
①初始化条件
②循环条件  变量或表达式为boolean
③循环体
④迭代部分

2、while的格式
①
do{
③
④
}while(②);

执行过程:①→③→④→②→③→④...→②

3、说明:
①do-while循环至少执行一次循环体
②三循环可以相互转换
③开发中,do-while结构使用少

3.2 for循环

循环结构之一:for循环

1、java中规范了3种循环结构:for、while、do-while
2、凡是循环结构,就一定会有4个要素
①初始化条件
②循环条件  变量或表达式为boolean
③循环体
④迭代部分

3、for循环的格式

for(①;②;④){
   ③;
}
   
执行过程:①→②→③→④→②→③→...→②
“无限”循环的结果的使用

1、格式:while(true)或for(;;)

嵌套循环的使用

1、嵌套循环:是指一个循环结构A的循环体是另一个循环结构B
-外层循环:循环结构A
-内层循环:循环结构B

2、说明:
①内层循环充当外层循环的循环体
②两层嵌套循环,外层循环控制行数,内层循环处理列数
③外层执行m次,内层循环执行n,内层循环的循环体执行m*n次
④实际开发基本不会出现三层循环,三层的循环结构少见
	               //菱形	
//上半部分
		for(int i=1;i<=5;i++){
			for(int j=1;j<=10-2*i;j++){										System.out.print(" ");			
			}
			for(int k=1;k<=2*i-1;k++){										System.out.print("* ");			
			}
			System.out.println();
		}
//下半部分
		for(int i=4;i>=1;i--){
			for(int j=1;j<=10-2*i;j++){										System.out.print(" ");			
			}
			for(int k=1;k<=2*i-1;k++){										System.out.print("* ");			
			}
			System.out.println();
		}

3.3 跳转语句

break和continue关键字的使用
   
            使用范围         在循环结构中的作用                   相同点
break:    switch-case                               在此关键字的后面不能声明执行语句
            循环结构中     结束(或跳出)当前循环结构
continue:  循环结构中     结束(或跳出)当次循环

2、带标签的break和continue
3、开发中,break的使用频率要远高于continue

3.4 Scanner基本使用

如何从键盘获取不同类型(基本数据类型、String类型)的变量:使用Scanner类

1、使用Scanner获取不同类型数据的步骤
步骤1:导包 import java.util.Scanner;
步骤2:提供(或创建)一个Scanner类的实例 

Scanner sc = new Scanner(System.in);//sc为类名自定义

步骤3:调用Scanner类中的方法,获取指定类型的变量

int num = sc.nextInt();//使用int类型接收数据,还可以使用其它类型

步骤4:关闭资源,调用Scanner类的close();方法

四、数组

4.1 一维数组

1、数组的概念(Arrays)

概念:多个相同类型数据按一定数据按一定顺序排列的组合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理

简称:多个数据组合

java中的容器:数组、集合框架:在内存中对多个数据储存

2、几个相关概念
>数组名
>数组的元素(即内部储存的多个元素)
>数组的下标、角标、下角标、索引、index(即找到指定数组元素所使用的编号)
>数组长度(即数组容器中储存的元素个数)

3、数组的特点:
>数组中的元素在内存中是依次紧密排列的、有序的。
>数组属于引用数据类型的变量,数组元素既可以是基本数据类型也可以是引用数据类型
>数组,一旦初始化完成,其长度不可改变
>创建数组对象会在内存中开辟一整块“连续的空间”。占据的空间的大小,取决于数组的长度和数组中元素的类型。

复习:变量按照数据类型分类

4.1 基本数据类型:byte、short、int、long、float、double、char、Boolean

4.2 引用数据类型:类、数组、接口、枚举、注解、记录

5、数组的分类

5.1 按照元素的类型:基本数据类型元素的数组,引用数据类型元素的数组

5.2 按照数组的维数来分:一维数组、二维数组

6、一维数组的使用(6个基本点)
>数组的声明和初始化
>调用数组的指定元素
>数组的属性:length,表示数组的长度
>数组的遍历
>数组的默认初始化值
>一维数组的内存解析

7、数组元素的默认初始化值的情况
注意:以数组的动态初始化方式为例
>整数型数组元素的默认初始化值:0
>浮点型数组元素的默认初始化值:0.0
>字符型数组元素的默认初始化值:0或'\u0000'
>布尔型数组元素的默认初始化值:false
>引用型数组元素的默认初始化值:null

8、一维数组的内存解析
8.1 java中的内存结构是如何划分的?(主要关心JVM运行时的内存环境)
>将内存区域划分为5个部分:程序计数器、虚拟机栈、本地方法栈、堆、方法区
>与目前数组相关的结构:比如:int[] arr = new int[]{1,2,3};
    >虚拟机栈:用于存放方法中声明的变量。比如:arr
    >堆:用于存放数组的实体(即数组中的所有元素)。比如:1,2,3

栈(stack) 堆(heap)

基本使用

        //1.数组的声明和初始化
        //复习:变量的定义格式:数据类型 变量名 = 变量值;
        int num1= 10;

        //声明数组
        double[] prices;
        //数组的初始化
        //静态初始化:数组变量的赋值与数组元素的赋值操作同时进行
        prices = new double[]{20.32,43.21,43.22};

        //数组声明和初始化
        //动态初始化:数组变量的赋值与数组元素的赋值操作分开进行
        String[] foods = new String[4];

        //其它正确方式
        int arr[] = new int[]{1,2,3,4};
        //类型推断
        int arr1[] = {1,2,3};

        //2.数组元素的调用
        //通过角标的方式,获取数组的元素
        //角标范围从0到数组长度-1
        System.out.println(prices[0]);
        System.out.println(prices[2]);
        //System.out.println(prices[3]);数组越界:ArrayIndexOutOfBoundsException

        foods[0] = "拌海蜇";
        foods[1] = "龙须菜";
        foods[2] = "炝冬笋";
        foods[3] = "玉兰片";

        //3.数组长度:用来描述数组容器中容量的大小
        //使用length属性表示
        System.out.println(prices.length);
        System.out.println(foods.length);

        //4.如何遍历数组
        for (int i = 0; i < foods.length-1; i++) {
            System.out.println(foods[i]);
        }

4.2 二维数组

1、二维数组的理解

- 对于二维数组的理解,可以看成一个一维数组又作为另一个一位数组的元素存在。
- 其实,从底层运行机制来看,没有多维数组。

2、二维数组的使用(6个基本点)

>二数组的声明和初始化
>调用二数组的指定元素
>二数组的属性:length,表示数组的长度
>二数组的遍历
>二数组的默认初始化值
>二维数组的内存解析

3、二维数组元素的默认初始化值

3.1 动态初始化方式1:(比如:int[][] arr = new int[3][4];)

外层元素,默认储存地址值
内层元素
>整数型数组元素的默认初始化值:0
>浮点型数组元素的默认初始化值:0.0
>字符型数组元素的默认初始化值:0或'\u0000'
>布尔型数组元素的默认初始化值:false
>引用型数组元素的默认初始化值:null


3.2 动态初始化方式2:(比如:int[][] arr = new int[3][];)

外层元素,默认储存null
内层元素,不存在的。如果调用报错(NullPointerException)

基本使用

        //1.数组的声明和初始化
        //复习:
        int arr1[] = new int[]{1,2,3};

        //声明数组
        //数组的初始化
        //静态初始化:数组变量的赋值与数组元素的赋值操作同时进行
        int arr2[][] = new int[][]{{1,2,3},{4,5},{6}};

        //数组声明和初始化
        //动态初始化:数组变量的赋值与数组元素的赋值操作分开进行
        String[][] arr3 = new String[4][3];
        //动态初始化2:
        double[][] arr4 = new double[4][];

        //其它正确方式
        int[][] arr5 = new int[][]{{1},{2}};
        int[] arr6 []= new int[][]{{1},{2}};
        //类型推断
        int[][] arr7 ={{1},{2}};

        //2.数组元素的调用
        //通过角标的方式,获取数组的元素
        //角标范围从0到数组长度-1
        //  针对arr2来说,外层元素:{1,2,3},{4,5,6},{7,8,9},内层元素:123456789
        //调用内层元素
        System.out.println(arr2[0][0]);//7
        //调用外层元素
        System.out.println(arr2[0]);//[I@1b6d3586

        //测试arr3、arr4
        arr3[0][0]="tom";
        System.out.println(arr3[0][0]);//tom
        System.out.println(arr3[0]);//[Ljava.lang.String;@4554617c

        arr4[0] = new double[4];
        arr4[0][0] = 1.0;

        //3.数组长度:用来描述数组容器中容量的大小
        //使用length属性表示
        System.out.println(arr2.length);//根据外层长度(元素个数)判断
        System.out.println(arr2[0].length);//3
        System.out.println(arr2[1].length);//2
        System.out.println(arr2[2].length);//1

        //4.如何遍历数组
        for (int i = 0; i < arr2.length; i++) {
            for (int j = 0; j < arr2[i].length; j++) {
                System.out.print(arr2[i][j] + "\t");
            }
            System.out.println();
        }

五、方法

1、方法的声明语法

方法是为了完成一个功能而组合在一起的语句块

方法的声明由方法名、参数、返回值类型及函数体组成。声明如下:

修饰符 返回值类型 方法名(参数列表)[throws 异常列表]{

               //方法体 

}

方法头:定义方法的访问特性(如:public)、使用特性(如:static)、返回值类型、方法名称、参数和抛出异常等
 

方法的返回值

通过return语句将方法的结果返回给调用者,return语句返回的值类型和函数头中声明的返回值类型要一致,void类型没有返回值

方法的参数

方法头中的变量称形式参数简称形参,方法可以不包含参数,没有参数的方法称为无参方法

2、方法的调用
方法名(实际参数值); 无参方法直接调用,不用传参

方法的参数传递

如果参数是基本数据类型,实参单元和形参单元存储的均为数据本身。参数传递就是将实参单元的数据复制给形参单元,在方法内修改形参的值,不影响实参

如果参数是数组或对象,则参数单元存放的是引用地址,也就是将实参单元存放的地址复制给形参单元,实参和形参将指向同一数据或对象。对形参数组或对象的操作访问,实际上是对实参数组或对象的操作访问。因此,在方法内修改参数的内容将影响实参 

3、递归调用

递归就是方法调用自身,这个过程称为递归,调用自己的方法称为递归方法

递归的两个基本要素

边界条件,显示确定递归到何时终止,也称为递归出口

递归模式,显示大问题是如何分解为小问题,也称为递归体

六、面向对象

6.0 初始面向对象

1.面向对象内容的三条主线
- Java类及类的成员:(重点)属性、方法、构造器;(熟悉)代码块、内部类
-面向对象的特征:封装 、继承、多态、(抽象)
-其他关键字的使用:this、super、package、import、static、final、interface、abstract等

2.面向过程编程(POP) VS 面向对象编程(OOP)

2.1 简单的语言描述二者区别

>面向过程(Process-Oriented Programming):
        -以函数为组织单位
        -是一种执行者思维,适合解决简单问题。扩展能力差、后期维护难度较大

>面向对象(Object Oriented Programming):
        -以类为组织单位。每种事物都具备自己的属性和行为/功能。
        -是一种设计者思维。适合解决复杂问题。代码扩展性强、可维护性高

2.2 二者关系
相辅相成

3.
>面向对象的两个概念:
    类:具有相同特征的事物的抽象描述,是抽象的、概念上的定义。
    对象:实际存在的该类事物的每个个体,是具体的,因而也称为实例(instance)。
>谈谈对这两个概念的理解?

4. 类的声明和使用
4.1 体会:设计类,其实就是设计类的成员
class person{

}

4.2 类的内部成员一、二
成员之一:属性、成员变量、field(字段、域)
成员之二:(成员)方法、函数、method

4.3 类的实例化
等价描述:类的实例化 <=> 创建类的对象 <=> 创建类的实例
格式:类类型 对象名 = 通过new创建的对象实体
举例:
Phone p1 = new Phone();
Scanner sc = new Scanner(System.in);
String str = new String();

5. 面向对象完成具体功能的操作的三步流程(非常重要)
步骤1.创建类,并设计类的内部成员(属性、方法)
步骤2.创建类的对象。比如:Phone p1 = new Phone();
步骤3.通过对象,调用其内部声明的属性或方法,完成相关功能
类的成员之一:属性

1.变量的分类
-角度一:按数据类型来分:基本数据类型(8种)、引用数据类型(数组、类、接口、枚举、注解、记录)
-角度二:按照变量在类中声明的位置的不同:成员变量(属性)、局部变量(方法内、方法形参、构造器内、构造器形参、代码块内等)

2.属性的几个称谓:成员变量、属性、field(字段、域)

3.区分成员变量 VS 局部变量
3.1 相同点
>变量声明的格式相同:数据类型 变量名 = 变量值
>变量都有其作用域。出了作用域就失效了。
>变量必须先声明后赋值

3.2 不同点:
①类中声明的位置不同
    属性:声明在类内,方法外的变量
    局部变量:声明方法、构造器内部的变量

②在内存中分配的位置不同(难点):
    属性:随着对象的创建,存储在堆空间
    局部变量:存储在栈空间

③生命周期:
    属性:随着对象的创建而创建,随着对象的消亡而消亡。
    局部变量:随着方法对应的栈针入栈,局部变量会在栈中分配;随着方法对应的栈针出栈,局部变量消亡。

④作用域:
    属性:在整个类的内部都是有效的
    局部变量:仅限于声明此局部变量所在的方法(或构造器、代码块)中

⑤是否可以有权限修饰符进行修饰:(难)
    都有哪些权限修饰符:public、protected、缺省、private。(用于表名所修饰的结构可调用的范围的大小)

    属性:可以使用权限修饰符进行修饰
    局部变量:不能使用任何权限修饰符进行修饰

⑥是否有默认值:
    属性:都有初始化默认值
    如果调用之前没有初始化,使用默认初始化值

    局部变量:都没有默认初始化值
    如果调用之前没有初始化值,直接报错,使用前必须赋值
    注意:对于方法的形参,在调用方法时,给形参赋值即可
类的成员之二:方法(method)

1、使用方法的好处

方法的理解:‘方法’是类或对象行为特征的抽象,用来完成某个功能操作
方法的好处:实现代码重用,减少冗余,简化代码

2、使用举例

-Math.random()的random()方法
-Math.sqrt()的sqrt()方法
-System.out.println()println()方法
-new Scanner(System.in) .nextInt()方法
-Arrays类中的binarySearch()方法、equals()方法

3、声明举例
    public void eat()
    public void sleep(int hour)
    public String interests(String hobby)

4、方法声明格式

权限修饰符[其他修饰符] 返回值类型 方法名(形参列表)[异常类型]{
    //方法体
}

注:[]中的不是必须的

5、具体的方法声明的细节

5、1权限修饰符
    java规定的修饰符 有四种:private\缺省\protected\public

5、2返回值类型:描述调用完此方法时,是否需要返回一个结果
       分类:
       >无返回值类型,使用void表示即可
       >有返回值类型,使用基本数据类型或引用数据类型表示即可
            >需要使用return + 返回值类型的变量或常量

5、3方法名:属于标识符。需要满足标识符的规定和规范。“见名知意”

5、4形参列表:形参,属于局部变量。
        格式:(形参类型1,形参1,形参类型2,,形参2,...)
        分类:无形参列表,有形参列表
        >无形参列表,不能省略()
        >有形参列表,根据方法调用需要不确定变量的类型和个数,确定形参的类型和个数

5.5方法体:当我们调用一个方法时,真正执行的代码。体现了此方法的功能

6、注意点
>java方法‘不能独立存在’,所有方法必须定义在类里
>java方法不调用不执行,每调用一次,执行一次
>java方法内可以调用本类中的(其它)方法或属性,方法内不能定义方法

7、关键字:return
7.1return的作用
    -作用1:结束一个方法
    -作用2:结束一个方法的同时,可以返回数据给方法的调用者

7.2注意点
    return后面不能声明执行语句

方法调用内存解析:
-形参:方法在声明时,一对()内声明一个或多个形式参数,简称为形参
-实参:方法被调用时,传递给形参的变量或常量,称为实参

过程概述:main方法进栈成栈针 → 生成 类 → 赋值 → 调用方法 方法调用完后出栈 → 执行 main方法 执行完成后 → 垃圾回收
类的成员之三:构造器(constructor)

1.构造器的理解
constructor:建造、构造、建设

2.构造器的作用
作用1:搭配new关键字,创建类对象
作用2:在创建对象的同时,可以给对象的相关属性赋值

3.构造器的使用说明
-构造器的声明格式:权限修饰符 类名(形参列表){}
-创建类以后,在没有显示提供任何构造器的情况下,系统默认提供一个空参的构造器
 ,且构造器的权限 与类声明的权限相同。
-如果类中显示声明构造器,系统不在提供默认的空参的构造器
-一个类中可以声明多个构造器,彼此之间构成重载

6.1 封装性

面向对象特征之一:封装性

1.为什么需要封装?
理论上:
    -高内聚:类的内部数据操作细节自己完成,不允许外部干涉
    -低耦合:仅暴露少量的方法给外部使用,尽量方便外部调用

通俗的说:把该隐藏的隐藏起来,该暴露的暴露出来。

2.如何实现数据封装?

2.1 权限修饰符
    java规定了4种权限修饰符,分别是:private、缺省、protected、public
    本类内部(private)、本包内(缺省)、其他包的子类(protected)、其他包非子类(public)
    每一个修饰符所在位置,包含前一个的范围

2.2 作用
    我们可以使用4种权限修饰符来修饰类及类的内部成员。当这些成员被调用时,体现可见性的大小

2.3 实际案例:
    在题目中给Animal的对象legs属性赋值。在实际常识中,legs不能为负数,如果直接调用属性legs
    不能加入判断逻辑,解决如下:
    -将legs属性私有化(private),禁止在Animal类的外部直接调用此属性
    -提供legs属性赋值的setLegs()方法,在此方法中加入legs属性赋值的判断逻辑if(legs >= 0 && legs % 2 == 0)
     将此方法暴露出去,使得在Animal类的外部调用此方法,对legs赋值
    -提供legs属性获取的getLegs()方法,此方法对外暴露,使得在Animal类的外部还可以调用此属性的值

2.4 4种权限具体使用
    -类:只能使用public、缺省修饰
    -类的内部成员:可以使用4种修饰符

2.5 开发中4种权限修饰符的使用情况
    比较高:public、private
    比较低:缺省、protected

3 封装性的体现
-场景一:私有化(private)类的属性,提供公共(public)的get和set方法,对此属性进行获取或修改
-场景二:将类中不需要对外暴露的方法,设置private
-场景三:单例模式中构造器private,避免在类的外部创建实例
this关键字的使用

1.目前可能出现的问题?以及解决方案?
当声明一个属性对应的SetXxx方法时,通过形参给对应的属性赋值。如果形参名和属性名同名,如何在方法中区分变量?

解决方案:使用this。使用this修饰的变量,表示属性,没有this修饰的变量,表示形参

2.this可以调用的结构:成员变量、方法、构造器

3.this的理解:当前对象(方法调用时)或当前正在创建的对象(在构造器调用时)

4.1 this调用属性和方法
针对于方法内的使用情况(非static修饰的方法):

一般情况:我们通过对象a调用方法,可以在方法内调用当前对象a的属性或其他方法。此时,我们可以在属性和其他方法前使用“this.”,
表示当前属性或方法所属的对象a。但是,一般情况下,我们都选择省略次“this.”结构

特殊情况:如果方法的形参与对象属性同名了,我们必须使用“this.”进行区分。使用this.修饰的变量即为属性(或成员变量)
没有使用this.修饰的变量,即为局部变量

针对于构造器内的使用情况:

一般情况:我们通过构造器创建对象时,可以在构造器内调用当前正在创建的对象的属性或方法。此时,我们可以在属性和方法前使用“this.”,
表示当前属性或方法所属的对象a。但是,一般情况下,我们都选择省略次“this.”结构

特殊情况:如果构造器的形参与正在创建的对象的属性同名了,我们必须使用“this.”进行区分。使用this.修饰的变量即为属性(或成员变量)
没有使用this.修饰的变量,即为局部变量

4.2 this调用构造器
-格式:“this(形参列表)”
-我们可以在类的构造器中,调用当前类中指定的其它构造器
-要求:“this(形参列表)”必须声明在当前构造器的首行
-结论:“this(形参列表)”在构造器中最多声明一个
-如果一个类中声明了n个构造器,则最多有n-1个构造器可以声明有“this(形参列表)”的结构

6.2 继承性

面向对象特征之二:继承性

1.继承性的理解
-生活上:财产、血缘的继承
-代码层面
    -自上而下:定义一个A类,在定义另一个B类时,发现B类的功能与A类相似,考虑B类继承于A类
    -自下而上:定义了类B、C、C等,发现B、C、D有类似的属性和方法,则可以考虑将相同的属性和方法进行抽取,
     封装到A类中,让类B、C、D继承于类A,同时类B、C、D中的相似的功能就可以删除了

2.继承性的好处
-继承的出现减少了代码的冗余,提高了代码的复用性
-有利于功能的扩展
-继承的出现让类与类之间产生了‘is-a’的关系,为多态提供了前提
-继承描述事物之间的所属关系‘is-a',父类更通用、一般,子类更具体

3.继承性的格式
class A{

}

class B extends A{

}

继承中的基本概念
-类A:父类、supperClass、超类、基类
-类b:子类,subClass、派生类

4.有了继承性以后
-子类获取父类中声明的所有属性和方法
-由于封装性的影响,可能子类不能直接调用父类中声明的属性或方法
-子类在继承父类以后,还可以扩展自己特有的功能(体现:增加特有的属性、方法)
 extends:延展、扩展,延伸
-继承之前,判断一下是否有is a的关系
-子类和父类,要区分集合和子集

5.默认的父类
java中声明的类,如果没有显示的声明其父类时,则默认继承于java.lang.Object

6.补充说明
-java支持多层继承
-直接父类、间接父类
-java中的子类的概念是相对的
-java中的一个父类可以声明多个子类,一个子类只有一个父类(java的单继承性)
一、super关键字的使用

1.为什么需要super?
子类需要继承父类的属性、方法

如何调用?使用super关键字

2.super的理解:父类的

3.super可以调用的结构:属性、方法、构造器

3.1 super调用属性、方法
-子类继承父类以后,我们就可以在子类的方法或结构中,调用父类中声明的属性或方法(满足封装性的前提下)
-可使用“super.”的结构,表示调用父类的属性或方法
-一般情况下,我们可以考虑省略“super.”的结构,但是,如果出现子类重写父类的方法或子父类中出现相同的属性或方法,
 必须使用“super.”声明,显示调用父类被重写的方法或父类中声明的同名的属性

3.2 super调用构造器
①子类继承父类时,不会继承父类的构造器。只能通过“super.(形参列表)”,调用父类指定的构造器
②规定:“super(形参列表)”,必须声明在构造器的首行
③在构造器的首行可以使用“this(形参列表)”,调用本类中重载的构造器
 在构造器的首行,“this(形参列表)”和“super(形参列表)”只能二选一
④如果子类构造器的首行既没有调用“this(形参列表)”也没有调用“super(形参列表)”,
  则子类构造器默认调用“super(形参列表)”,即调用父类中空参的构造器
⑤子类的任何一个构造器,要么调用本类中重载的构造器,要么调用父类的构造器
⑥一个类声明n个构造器,最多有n-1个构造器中使用“this(形参列表)”
  剩下那个一定使用“super(形参列表)”

--> 我们在通过子类的构造器创建对象时,一定在调用子类构造器的过程中,直接或间接调用到父类的构造器
    调用父类构造器,才能将父类中声明的属性或方法加载到内存中,供子类对象使用

二、子类对象实例化过程

class Creature{//生物类
    //属性、方法、构造器
}
class Animal extends Creature{//动物类

}
class Dog extends Animal{//狗类

}
class DogTest{
    public static void main(String args){
        Dog dog = new Dog();
    }
}

1.从结果角度来看:类的继承性

创建对象后,子类对象就获取了其父类中声明的所有属性和方法,在权限允许的情况下可以直接调用

2.从过程角度来看:

当通过子类的构造器创建对象时,子类的构造器一定会直接或者间接的调用到其父类的构造器,父类也会一层一层往父类调用,
直到调用默认父类Object中的构造器为止

3.创建子类对象的过程中,内存中到底有几个对象
  只有一个对象!即为当前new后面构造器对应的类的对象

6.3 抽象类与接口

1. abstract的概念:抽象的

2. abstract可以用来修饰:类方法

3. 具体使用:

3.1 abstract修饰类
    -此类称为抽象类
    -抽象类不能实例化
    -抽象类包含构造器,子类对象实例化,需要直接或间接的调用到父类构造器
    -抽象类中可以没有抽象方法,有抽象方法所在的类,一定是抽象类

3.2 abstract修饰方法
    -此方法称为抽象方法
    -抽象方法只有方法的声明,没有方法体
    -抽象方法其功能是确定的(方法声明确定),只是不知道如何实现(体现没有方法体)
    -子类必须重写父类中的所有抽象方法之后,才可以实例化。否则,子类依然是一个抽象类

4. abstract不能使用的场景:

4.1 abstract不能修饰哪些结构?
    属性、构造器、代码块等。

4.2 abstract 不能与哪些关键字共用?(自洽)
    私有方法、静态方法、final的方法、final类。
    -私有方法不能重写
    -避免静态方法使用类进行调用
    -final的方法不能被重写
    -final修饰的子类不能有子类
接口的使用

1. 接口的理解:接口的本质是契约、标准、规范

2. 定义接口的关键字:interface

3. 接口内部结构的说明:
    -可以声明:
        属性:必须使用public static final修饰(全局变量)
        方法:jdk8之前:只能声明抽象方法,修饰为public static
             jdk8:声明静态方法、默认方法
             jdk9:声明私有方法

    -不可以声明:构造器、代码块等

4. 接口与类的关系:实现关系

5. 格式:class A extends SuperA implements B,C{}

A相较于SuperA叫做子类
A相较于B,C叫做实现类

6. 满足此关系之后,说明:
-类可以实现多个接口
-类针对于接口的多实现,一定程度上弥补了类的单继承性的局限性
-类必须将实现的接口中的所有抽象方法都重写(实现),方可实例化。否则,此实现类必须声明为抽象类

7. 接口与接口的关系:继承关系,可以多继承

8. 接口的多态性:接口名 变量名 = new 实现类对象;

9. 区分抽象类和接口

-共性:都可以声明抽象方法
      都不能实例化

-不同:① 抽象类一定有构造器,接口没有构造器
     ② 类与类之间是继承关系,类与接口之间是实现关系,接口与接口之间是多继承关系

6.4 类的多态

面向对象特征之三:多态性

1.如何理解多态性?

理解:一个事物的多种形态

2.java中多态性的体现:
  子类对象的多态性:父类的引用指向子类的对象

3.多态性的应用:
    虚拟方法调用
在多态的场景下,调用方法时
    编译时,认为方法是左边声明的父类的类型的方法(即被重写的方法)
    执行时,实际执行的是子类重写父类的方法
简称:编译看左边,运行看右边

4.多态性的使用前提:
① 要有类的继承关系
② 要有方法的重写

5.多态的适用性:
使用于方法,不适用于属性

6.多态的好处与弊端
6.1弊端
在多态的场景下,我们创建子类的对象,也加载了子类特有的属性和方法。但是由于声明为父类的引用,
导致我们没有办法直接调用子类特有的属性和方法

6.2好处
极大的减少了代码的冗余,不需要定义多个重载的方法

7.instanceof的使用
-向下转型使用instanceof进行判断,避免出现转换异常类
-格式 a instanceof A :判断对象a是否是类A的实例
-如果 a instanceof A 返回true,则 a instanceof superA 返回也是true,A 是 super A 的子类

6.5 类的高级特性

static关键字的使用

1. static:静态的
   声明变量和方法时,加上static修饰,称为类变量和类方法(或静态变量和静态方法),实现成员和实例之间的共享。

2. static用来修饰的结构:属性、方法、代码块、内部类

3. static修饰属性
    3.1 复习:变量的分类
    方式1:按照数据类型:基本、引用

    方式2:按照类中声明的位置:
    成员变量:按照是否使用static修饰进行分类
            使用static修饰的成员变量:静态变量、类变量
            不使用static修饰的成员变量,非静态变量、实例变量

    局部变量:方法内、方法形参、构造器内、构造器形参、代码块内等

    3.2 静态变量:类中的属性使用static进行修饰
        对比静态变量和实例变量:
        ① 个数
        -静态变量:在内存空间中只有一份,被类的多个对象共享
        -实例变量:类的每一个实例(或对象)都保存一份实例变量
        ② 内存位置
        -静态变量:jdk6及之前存在方法区(叫做永久代,jdk8及以后叫做元空间),jdk及以后放在堆空间
        -实例变量:存放在堆空间的对象实体中
        ③ 加载时机
        -静态变量:随着类的加载而加载,由于类只会加载一次,所以静态变量只有一份
        -实例变量:随着对象的创建而加载。每个对象拥有一份实例变量
        ④ 调用者
        -静态变量:可以被类和对象调用
        -实例变量:只能被对象调用
        ⑤ 判断是否可以调用 ---> 从生命周期的角度解释
                    类变量         实例变量
        类            √              ×
        对象          √              √

        ⑥ 消亡时机
        -静态变量:随着类的卸载而消亡
        -实例变量:随着对象的消亡而消亡

4. static修饰方法:(类方法、静态方法)
-随着类的加载而加载
-可以通过“类.静态方法”的方式直接调用静态方法
-静态方法内可以调用静态的属性或静态的方法
         不能调用非静态的结构(比如:属性、方法)
-非静态方法可以调用当前类中的静态结构(属性、方法)或非静态结构(属性、方法)
-static修饰方法内不能使用this和super

                    类方法         实例方法
        类            √              ×
        对象          √              √
final关键字的使用

1. final的理解:最终的

2. final可以用来修饰的结构:类、方法、变量

3. 具体说明:

3.1 final修饰类:此类表示不能被继承
    比如:String、StringBuffer、StringBuilder类

3.2 final修饰方法:表示方法不能被重写
    比如:Object类中的getClass()

3.3 final修饰变量:即可以修饰成员变量,也可以修饰局部变量
    此时的“变量”变成了“常量”,意味着一旦赋值,就不可以更改

    3.3.1 final修饰成员变量:有哪些位置可以给成员变量赋值?
          -显示赋值
          -代码块中赋值
          -构造器中赋值

    3.3.2 final修饰局部变量:一旦赋值就不能修改
          -方法内声明的局部变量:在调用局部变量前,一定需要赋值。而且一旦赋值,就不可更改
          -方法的形参:在调用此方法时,给形参进行赋值。而且一旦赋值,就不可更改

4. final与static的搭配:修饰成员变量时,次变量称为:全局变量
                      比如:Math.PI
类的成员之四:代码块

回顾:类中可以声明的结构:属性、方法、构造器、代码块(或初始化块)、内部类

1. 代码块(或初始化块)的作用
    用来初始化类或对象的信息(即初始化类或对象的成员变量)

2. 代码块的修饰:
    只能使用static

3. 代码块的分类:
    静态代码块:使用static
    非静态代码块:未使用static

4. 具体使用

4.1 静态代码块:
    -随着类的加载而调用
    -类加载执行一次,静态代码块也执行一次
    -作用:初始化类的信息
    -内部可以声明变量、调用属性或方法、编写输出语句等操作。
    -静态代码块执行先于非静态代码块
    -如果声明多个静态代码块,则按照声明的先后顺序执行
    -静态代码块内部只能调用静态的结构(即静态的属性、方法),不能调用非静态的结构(即非静态属性、方法)

4.2 非静态代码块:
    -随着对象的创建而执行
    -每创建当前类的实例,就会执行一次非静态代码块
    -作用:初始化对象的信息
    -内部可以声明变量、调用属性或方法、编写输出语句等操作。
    -如果声明多个非静态代码块,则按照声明的先后顺序执行
    -静态代码块内部可以调用静态的结构(即静态的属性、方法),也可以调用非静态的结构(即非静态属性、方法)
类的成员之五:内部类

1. 什么是内部类?
将一个类A定义在另一个类B里面,里面的那个类A叫做内部类(InnerClass),外面的类B叫做外部类(OuterClass)

2. 为什么需要内部类?
当一个类只服务于另一个类时,推荐定义内部类,遵循高内聚、低耦合的面向对象开发原则

3. 内部类的使用举例
Thread类内部声明了State类,表示线程的生命周期
HashMap类中声明了Node类,表示封装的Key和Value

4. 内部类的分类:
    -成员变量:直接声明在外部类的里面
        -使用static修饰:静态的成员内部类
        -不使用static修饰:非静态的成员内部类

    -局部变量:声明在方法内、构造器内、代码块内的内部类
        -匿名的局部内部类
        -非匿名的局部内部类

5. 内部类知识:
    -成员内部类的理解
    -如何创建成员内部类的实例
    -如何在成员内部类中调用外部类的结构
    -局部内部类的基本使用

6. 关于成员内部类的理解:
    -从类的角度看:
        -内部可以声明属性、方法、构造器、代码块、内部类等
        -此内部类可以声明父类,可以实现接口
        -可以使用final修饰
        -可以使用abstract修饰

    -从外部类的成员角度看
        -在内部可以调用外部类的结构。比如:属性、方法等
        -除了使用public、缺省权限修饰之外,还可以使用private、protected修饰
        -可以使用static修饰

6.6 异常

异常处理概述

1. 什么是异常
    指的是程序在执行过程中,出现的非正常情况,如果不处理最终会导致JVM的非正常停止。

2. 异常抛出机制
    java中把不同的异常用不同的类表示,一旦发生某种异常,就创建该异常类型的对象,并且抛出(throw)。然后程序员可以捕获(catch)到这个异常对象,并处理;
    如果没有捕获(catch)这个异常对象,那么这个异常对象将会导致程序终止。

3. 如何对待异常
    对于程序出现的异常,一般有两种解决方法:一是遇到错误就终止程序的运行。另一种方式是程序员在编写程序时,
    就充分考虑到各种可能发生的异常和错误,极力预防和避免。实在无法避免的,要编写相应的代码进行异常检测,
    以及‘异常的处理’,保证代码的‘健壮性’。

4. 异常的体系结构
java.lang.Throwable:异常体系的根父类
    -java.lang.Error:错误。java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况
                    StackOverFlowerError、OutOfMemoryError
    -java.lang.Exception:异常。我们可以编写针对性的代码进行处理
                    编译时异常:(受检异常)在执行javac.exe命令时,出现的异常。
                        -ClassNotFoundException
                        -FileNotFoundException
                        -IOException
                    运行时异常:(非受检异常)在执行java.exe命令时,出现的异常。
                        -ArrayIndexOutOfBoundsException
                        -NullPointerException
                        -ClassCastException
                        -NumberFormatException
                        -InputMismatchException
                        -ArithmeticException
1. 方式一(抓抛模型):try-catch-finally

过程1:“抛”
    程序在执行的过程中,一旦出现异常,就会在出现异常的代码处,生成对应异常类的对象,并将此对象抛出
    一旦抛出,此程序就不在执行其它的代码了

过程2:“抓”
    针对于过程1中抛出的异常对象,进行捕获处理。此捕获处理的过程,就称为抓
    一旦将异常进行了处理,代码就可以继续执行

2. 基本结构:
try{
    ...... //可能产生异常的代码
}
catch(异常类型一 e){
    ...... //当产生异常类型1型异常时的处置措施
}
catch(异常类型二 e){
    ...... //当产生异常类型2型异常时的处置措施
}
finally{
    ...... //无论是否发生异常,都无条件执行的语句
}

3. 使用细节:
-将可能出现异常的代码声明在try语句中。一旦代码出现异常,就会自动生成一个对应异常类的对象。并将此对象抛出。
-针对于try中抛出的异常类的对象,使用之后的catch语句进行匹配。一旦匹配上,就进入catch语句块进行处理。
 一旦处理接触,代码就可以继续执行下去
-如果声明了多个catch结构,不同的异常类型在不存在子父类关系的情况下,声明没有顺序要求
 如果多个异常类型满足子父类的关系,则必须将子类声明在父类结构的上面。否则,报错。
-catch中异常处理的方式:
    ①自己编写输出的语句。
    ②printStackTrace():打印异常的详细信息。(推荐)
    ③getMessage():获取发生异常的原因
-try catch可以嵌套使用

4. 开发体会:
    -对于运行时异常:
        开发中,通常就不进行显示的处理了
        一旦在程序执行中,出现了运行时异常,那么就根据异常的提示信息修改代码即可

    -对于编译时异常:
        一定要处理。否则编译不通过。

5. finally的使用说明:
5.1 finally的理解
-将一定要被执行的代码声明在finally结构中
-无论在try中或catch中是否存在任未被处理的异常,无论try中或catch中是否存在return语句等
 finally中声明的语句都一定要被执行
-finally语句和catch语句是可选的,但finally不能单独使用


5.2 什么样的代码我们一定要声明在finally中呢?
-开发中一些资源(比如:输入流、输出流、数据库连接、Socket连接等资源),在使用完以后,必须显式的进行关闭操作,
否则,GC不会自动的回收这些资源。进而导致内存的泄漏
 为了保证这些资源在使用完以后,不管是否出现了未被处理的异常情况下,这些资源能被关闭。我们必须将这些操作声明在finally中
异常处理的方式2:throws
1. 格式:在方法的声明出使用“throws 异常类型1,异常类型2,...”

2. 举例:

public void test() throws 异常类型1,异常类型2,...{
    //可能存在编译时异常
}

3. 是否真正处理了异常?
-从编译是否通过的角度看,看成是给出了异常,万一要是出现时候的解决方案。此方案就是,继续往上抛(throws)
-但是,此throws的方式,仅是将可能出现的异常抛给了此方法的调用者。此调用者任然需要考虑如何处理相关异常
 从这个角度来看,throws的方式不算是真正意义上处理了异常。

4. 方法重写的要求:(针对于编译异常来说)
子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类。

6.7 常用类

1.String类的理解(以jdk8为例说明)
1.1 类的声明
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence

-final:String是不可被继承的
-Serializable:可序列化接口。凡是实现此接口的类的对象都可以通过网络或本地流进行数据的传输。
-Comparable:凡是实现此接口的类,其对象都可以比较大小。

1.2 内部声明的属性
jdk8中:
private final char value[];//储存字符串数据的容器
        -final:指明此value数组一旦初始化,其地址就不可变

jdk9开始:为了节省内存空间
private final byte[] value;

2. 字符串常量的储存位置
-字符串常量都储存在字符串常量池(StringTable)中
-字符串常量池不允许存放两个相同的字符串常量
-字符串常量池,在不同的jdk版本中,存放位置不同
 jdk7之前,字符串常量池存放在方法区
 jdk7及之后,字符串常量池,存放在堆空间

3. String的不可变性的理解

4. String实例化的两种方式

【面试题】
String s2 = new String("hello");在内存中创建了几个对象?
一个是堆空间中new的对象。另一个是在字符串常量池中生成的字面量

5. String的连接操作:+
情况1:常量 + 常量 :结果仍然储存在字符串常量池中,返回此字面量的地址。注:此时的常量可能是字面量,也可能是final修饰的常量
情况2:常量 + 变量 或 变量 + 常量 :都会通过new的方式创建一个新的字符串,返回堆空间中此字符串对象的地址值
情况3:调用字符串的intern() :返回的是字符串常量池中字面量的地址
(了解)情况4:concat():无论是常量调用方法,还是变量调用方法,无论参数是参量还是变量,调用完concat()方法都会返回一个新new的对象

6. String的构造器和常用方法
6.1 构造器
public String():初始化新创建的String对象,以使其表示空字符序列
public String(String original):初始化一个新创建的String对象,使其表示一个与参数相同的字符序列
public String(char value[]):通过当前参数中的字符数组来构造新的String
public String(char value[], int offset, int count):通过字符数组的一部分来构造新的String
public String(byte bytes[]):通过使用平台的默认字符集解码当前参数中的字节数组来构造新的String
public String(byte bytes[], String charsetName):通过使用指定的字符集解码当前参数中的字节数组来构造新的String
针对于StringBuilder来说:
内部属性有:
        char[] value;//储存字符序列
        int count;//实际储存的字符的个数

StringBuilder sb1 = new StringBuilder();//char[] value = new char[16];
StringBuilder sb1 = new StringBuilder("abc");//char[] value = new char[16+"abc".length()];

sb1.append("ac");//value[0] = 'a';value[1] = 'c';
sb1.append("b");//value[2] = 'b';

当count超过value.length()时,就需要扩容:默认扩容为原容量的2倍+2,并将原有value数组中的元素复制到新的数组中

3. 原码启示
-如果开发中需要频繁的针对于字符串进行增、删、改、查等,建议使用StringBuilder或StringBuffer替换String,因为String效率低
-如果开发中,不涉及 到线程安全问题,建议使用StringBuilder替换StringBuffer。因为StringBuilder效率高
-如果开发中大体确定要操作的字符的个数,建议使用带int capacity参数的构造器,避免底层多次扩容操作,性能降低

4. StringBuffer和StringBuilder中的常用方法

增:
    StringBuffer append(xx):提供了很多的append()方法,用于进行字符串追加的方式拼接
删:
    StringBuffer delete(int start,int end):删除[start,end]之间的字符
    StringBuffer deleteCharAt(int index):删除[index]位置的字符
改:
    StringBuffer replace(int start,int end,String str):替换[start,end)范围的字符序列
    void setCharAt(int index,char c):替换[index]位置字符
查:
    char charAt(int index):查找指定位置index位置上的字符
插:
    StringBuffer insert(int index,xx):在[index]位置上插入xx
长度:
    int length():返回储存的字符数据的长度
反转:
    StringBuffer reverse():反转

5. 对比三者的执行效率
效率从高到低排列:
    StringBuilder > StringBuffer > String


        /*
            注意:Date 日期 和 Data 数据   数据库:Database
            Java中针对时间日期类型提供了Date工具类。
            使用之前需要进行导包,和Scanner类非常类似: java.util.Date
            注意是util下的Date!
            
            导包:大部分的常用包都没有导入 ,原因:Java自带的一个包叫做 Java.lang.*包 该包下得类都是自动导入
            例如String  System
            
            需要的手动导入:Scanner
            
        */
        
        /*
         常见的获取Date对象方式如下:
            Date d = new Date();//获取当前时间
            常见的过时方法:···
        */
        
        
        
        Date d = new Date();//下面的方法是过时的,但是这个对象不过时
        System.out.println(d);//发现:结果是对中国人不友好的
        //所以需要通过大量的过时方法来对中国人民友好
        //过时方法:仅仅是针对  大量的用户   翻译:如果你的项目很多很多人使用,就不推荐使用下面的方法
        //如果用户数量较少,可以使用,但是不推荐。
//        int year = d.getYear();
//        System.out.println(year+1900);
//        int month = d.getMonth();
//        System.out.println(month + 1);
//        int date = d.getDate();
//        System.out.println(date);
//        int h = d.getHours();
//        int m = d.getMinutes();
//        int s = d.getSeconds();
//        System.out.println(h+":" + m + ":" + s
        /*
            由于Date对象的99%的方法都已经过时
            备注:Date d = new Date();还是没有过时,依然是获取当前时间的唯一方法。
            
            所以Java提供了SimpleDateFormat类,来替代Date的绝大部分方法,来对时间进行操作。
            Simple:简单的
            Date:日期
            Format:格式
            
            
            语法如下:
            SimpleDateFormat sdf = new SimpleDateFormat(“日期的具体格式”);
            日期具体格式如下:
            yyyy-MM-dd HH:mm:ss
            yyyy/MM/dd HH:mm:ss
            yyyy-MM-dd
            HH:mm:ss
            格式可以随便改,但是字母不能。yyyy表示年  MM表示月  dd日期   HH小时 mm分钟 ss秒
            
            注意:yyyy和MM 和dd和mm和ss  是固定的
            但是HH表示24小时制的时间
            hh表示12小时的时间
            
            其中字母定死了,符号可以根据场景改变。
            字母可以省略,例如只保留年月日或只保留时分秒。
        */
        
//        //如何获取当前时间
//        //1.定义时间的格式
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
//        //2.获取当前时间
//        Date d = new Date();
//        //3.格式化时间
//        String str = sdf.format(d);
//        System.out.println(str);
        
        
        
        
        //某些场景:可能需要过去或未来的时间 方法如下:
        String str = "2030/8/8   12:12:12";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date date = sdf.parse(str);
        System.out.println(date);

    /*
        Java提供了Math类来执行基本的数学运算。
        该类处于java.lang下,所以使用无需导包。
        该类下的所有方法都是静态方法,故无需new对象。
    */
    
    public static void main(String[] args) {
        //floor 地板
        //ceil  天花板
        
//        doule Math.floor(double x);  向下取整,但是依然是double类型。
        System.out.println(Math.floor(1.1));//1.0
        System.out.println(Math.floor(1.0));//1.0
        
        
//        double Math.ceil(double x);向上取整,但是依然是double类型
        System.out.println(Math.ceil(1.1));//2.0
        System.out.println(Math.ceil(1.0));//1.0
        
//        long Math.round();四舍五入
        System.out.println(Math.round(1.1));//1
        System.out.println(Math.round(1.5));//2
        System.out.println(Math.round(1.9));//2
        
        //总结:四舍五入如果碰到± xxx.5 优先往大的数走
        System.out.println(Math.round(-1.1));//-1
        System.out.println(Math.round(-1.5));//-1
        System.out.println(Math.round(-1.9));//-2
        
//        double Math.random();取一个大于等于0但是小于1的小数  官方概念是这样,但是实际上可以不用考虑0
        System.out.println(Math.random());
        
        
    }
    
}

/*
    学会看API(教学文档,可以了解一些方法的组成以及如何使用):语法如下:
    数据类型  方法名(参数列表);
    数据类型: 方法的返回值

        /*
            包装类是专门针对于基本数据类型而言。
            由于基本数据类型没有方法或属性。
            为了更加方便对基本数据类型的值进行操作,Java提供了八大基本数据类型的包装类。
            
            boolean   byte
            char      short
            int       float
            double    long
            
            
            Boolean    Byte
            Character  Short
            Integer    Float
            Double     Long
        */
        
        //包装类如何声明变量 和String一样
        Integer a = 123;//直接声明
        Integer b = new Integer(123);//通过构造方法声明
        
        //包装类的用途:
        //1.作为实体类属性的数据类型,替换基本数据类型。
        //以前的写法 见下方的Student类
        Student s = new Student(123, "张三", 18,18);
        Student s1 = new Student();
        System.out.println(s);
        System.out.println(s1);
        
        //缺点:基本数据类型的默认值为0   而引用数据类型的默认值为null
        
        //2.将字符串转为对应的基本数据类型
        String str = "123445";
        int i = Integer.parseInt(str);
        System.out.println(i);    
        double d = Double.parseDouble(str);

    /*
        包装类的补充:
        仅做了解为了面试 实际开发用不到
    */

    public static void main(String[] args) {
        //自动装箱值得是:将基本数据类型 变成  包装类
//        int a = 11;
//        Integer b = a;//此过程就是自动装箱
//        
//        //自动拆箱值得是:将包装类转成基本数据类型
//        Integer c = 1;
//        int d = c.intValue();
//        
        
        //常见面试题:
        
//        String str1 = "你好";
//        String str2 = "你好";
//        String str3 =  new String("你好");
//        System.out.println(str1==str2);//true
//        System.out.println(str1==str3);//f
//        
//        System.out.println(str1.equals(str2));//t
//        System.out.println(str1.equals(str3));//t
//        
//        System.out.println(str2==str3);//f
//        System.out.println(str2.equals(str3));//t
        
        //Integer和String的类似度高达99%
//        Integer str1 = 1;
//        Integer str2 = 1;
//        Integer str3 = new Integer(1);
//        System.out.println(str1==str2);//true
//        System.out.println(str1==str3);//f
//        
//        System.out.println(str1.equals(str2));//t
//        System.out.println(str1.equals(str3));//t
//        
//        System.out.println(str2==str3);//f
//        System.out.println(str2.equals(str3));//t
        
        Integer str1 = 1111;
        Integer str2 = 1111;
        Integer str3 = new Integer(1111);
        System.out.println(str1==str2);//false
        System.out.println(str1==str3);//f
        
        System.out.println(str1.equals(str2));//t
        System.out.println(str1.equals(str3));//t
        
        System.out.println(str2==str3);//f
        System.out.println(str2.equals(str3));//t
        
        
        //Integer直接声明变量和String非常类似 ,都有一个类似缓存池的存在
        //String直接声明:先去缓存池找是否存在,如果不存在就new一个,如果存在就直接引用
        //Integer直接声明:现在-128~127的缓存池找是否存在,如果不存在就new一个,如果存在就直接引用

6.8 集合类

            Collection:集合   Array:数组
            集合和数组非常类似,都是存储数据的一个容器。
            区别:
            数组是定长,且只能存储相同类型的数据。
            集合是变长,不仅可以存储相同数据而且可以存储不同类型的数据的容器。
            Java中的集合可以看成JavaScript的数组。仅仅是概念一样,代码完全不一样。
    
            备注:和集合相关的类都需要导包。注意都是java.util下。
            

            Java中的集合分为两大类:
            A.单列集合(类似数组,只需要存值,通过下标访问值)
            B.双列集合(类似对象,不仅需要存值,还要存与之对应的属性名,通过属性名访问属性值)
            
            Collection就是所有单列集合的父接口
            所有单列集合都实现了该接口,所以:所有单列集合都拥有共同的方法。
            所以学会了一种单列集合,剩下的单列集合都能依葫芦画瓢。

            Collection又分为:
            List子接口(内部元素:有序且可以重复)
            
            Set子接口(内部元素:无序且无法重复)


            List接口下主要分为以下两个实现类:
            
            ArrayList 集合:采用数组结构,元素增删慢,查找快,由于日常开发中使用最多的功能就是查询,所以 ArrayList 是最常用的集合之一。没有保证线程安全,但是效率高。
            
            Vector集合:    保证了线程安全,但是效率较低。除此和ArrayList都一样
            
            LinkedList 集合:采用链表结构。元素增删快,但是查找慢。

            线程安全:保证多个用户共同使用它也不会出错
            数组结构:元素增删慢,查找快
            链表结构:元素增删快,查找慢
            
            原因:
            数组结构是有序的,每个元素都有一个下标,查找的时候只需要根据下标查找即可,              所以元素的查找快,但是一旦发生了元素的增加或减少, 会引发每个元素的下标发生             变化,导致增删慢。
            
            链表结构是无序的,每个元素仅仅存储了本身的值,和引入下一个元素的地址。
            所以查询都是一个一个轮流查询,而不是像数组结构,直接根据下标查询。
            但是增删快,因为只需要对增删位置的元素进行修改即可,不会影响别的元素。

        //常见的集合1: ArrayList:
        //复习: 特点1:采用了数组结构:查询快。增删慢    特点2:线程非安全所以效率快
        //特点3:有序且可以重复    特点4:父类是List  祖父类:Collection
        
        
        //如何创建一个ArrayList :类似于多态:  左边放父类 右边放自类
        List  list = new ArrayList();
        
        
//        boolean add(Object o); //往集合尾部添加元素,一般不需要返回值  
        System.out.println(list.add("张三"));
        System.out.println(list.add("李四"));
        System.out.println(list.add("wangw"));
        System.out.println(list.add("jakc"));
//        void add(int index, Object o );//往集合指定位置添加元素
        list.add(2,"王五");
        System.out.println(list);
        
        
        //常见的方法:
//        int size();   //返回集合的长度
        System.out.println(list.size());
        
//        boolean isEmpty(); //判断集合的内部是否为null 注意:并不是判断集合为空 
        System.out.println(list.isEmpty());
        
//        boolean contains(Object o);  //判断集合内部是否存在该值
        System.out.println(list.contains("张三1"));
        

//        boolean remove(Object o);//删除集合指定的元素值
        System.out.println(list.remove(0));//根据下标删除
        System.out.println(list.remove("jakc"));
        System.out.println(list);
        
        
//        int indexOf(Object o);//返回指定元素首次出现的位置。-1表示不存在
        System.out.println(list.indexOf("王五123"));
        
        
//        boolean equals(Object o);//判断两个集合是否   值和顺序都相等
//        也重写了Object的equals方法

        
        
        //Object  get(index)  获取指定位置的元素
        System.out.println(list.get(2) + "~~");
        
        List l1 = new ArrayList();
        List l2 = new ArrayList();
        l1.add(123);
        l2.add(123);
        System.out.println(l1==l2);
        System.out.println(l1.equals(l2));


        //复习: 特点1:单列集合 父类是List 祖父类Collection
        //特点2:数组结构   特点:有序可重复   线程安全效率低
        
//        List list = new Vector();
//        list.add(true);
//        list.add("nihao");
//        System.out.println(list);
        
        
        //案例:测试ArrayList和Vector 同时增加100000个元素的效率差
        
        
//        long l1 = System.currentTimeMillis();
//        List list = new ArrayList();
//        for(int i = 1; i<=1000000;i++) {
//            list.add(i);
//        }
//        System.out.println(list.size());
//        long l2 = System.currentTimeMillis();//16毫秒
//        System.out.println(l2-l1);
        
//        long l1 = System.currentTimeMillis();
//        List list = new Vector();
//        for(int i = 1; i<=1000000;i++) {
//            list.add(i);
//        }
//        System.out.println(list.size());
//        long l2 = System.currentTimeMillis();//28毫秒
//        System.out.println(l2-l1);

        //LinkList:特点: 父类都是List祖父类都是Collection  有序 可重复
        //链表结构  增删快。查询慢
//        List list = new LinkedList();
        //常见的方法如上一样。
        
        //案例:测试ArrayList 和  LinkList 的效率
        
        List list1 = new ArrayList();
        List list2 = new LinkedList();
        for(int i = 1; i<=10000000;i++) {
            list1.add(i);
            list2.add(i);
        }
        
        long l1 = System.currentTimeMillis();
        int i = list1.indexOf(9999999);
        System.out.println(i);
        long l2 = System.currentTimeMillis();
        System.out.println(l2-l1);

        //Set集合接口: 特点:单列集合 父类依然是Collection
        //特点:无序无法重复。  该接口下有一个常见类:HashSet
        
        Set<Object>  set = new HashSet<Object>();
        
        set.add(true);
        set.add(123);
        set.add("你好");
        set.add(123);
        System.out.println(set);
        
        //如何遍历集合
        //如果List集合 普通for循环即可完成遍历
        
        //但是set集合  没有下标    增强for循环
        for(Object o :set ) {
            System.out.println(o);
        }
        
        //泛型的概念 :就是每个集合后面跟着的 <E>   E表示这个集合应该存储什么类型的数据
        //集合:可以存放不同类型的。但是即使这样,你也要声明
        
        List<Integer> list = new ArrayList<Integer>();
        list.add(12345);
        System.out.println(list);

        //Map集合双列集合:   分别存储 键 和 值    键理解为属性名  value属性值
        //键绝对是独一无二的   一般来说键:String   值:Object  
        Map<String,Object> map = new HashMap<String,Object>();
        
        map.put("1001", "张三1");//添加元素
        map.put("1002", "张三2");
        map.put("1003", "张三3");
        System.out.println(map);
        
        System.out.println(map.get("1002"));//获取指定键 对应的值
        System.out.println(map.size());
        
        //如何遍历一个map集合
        //1.将其转成Set集合  
        Set<String> set = map.keySet();//将map集合中的键 全部转成set集合
        System.out.println(set);
        
        for(String s :set) {
            System.out.println(map.get(s));
        }

        //使用3种方式 遍历集合的效率
        
        List<Integer> list = new ArrayList<Integer>();
        
        for(int i = 1; i<=10000000;i++) {
            list.add(i);
        }
        
        System.out.println("准备完毕!!!!!!!!!!");
        
        
//        遍历集合的效率
        long l1 = System.currentTimeMillis();
        for(int i = 0 ;i<list.size();i++) {
            if(i==9999999) {
                System.out.println("111222");
            }
        }
        long l2 = System.currentTimeMillis();
        System.out.println("普通for循环的遍历效率为" +  (l2-l1) );//7
        
        
        long l3 = System.currentTimeMillis();
        for(Integer i : list) {
            if(i==9999999) {
                System.out.println("111222");
            }
        }
        long l4 = System.currentTimeMillis();
        System.out.println("增强for循环的遍历效率为" +  (l4-l3) );//35
        
        
        long l5 = System.currentTimeMillis();
        //任何集合都可以通过额外一个知识点: Iterator(迭代器)
        Iterator<Integer>  it = list.iterator();
        while(it.hasNext()) {//是否还有下一个元素
            if(it.next()==99999999) {
                
            }
        }
        long l6 = System.currentTimeMillis();
        System.out.println("迭代器循环的遍历效率为" +  (l6-l5) );//23

        Set<Student> set = new HashSet<Student>();
        Student s1 = new Student(1, "jack1", 12);
        Student s2 = new Student(2, "jack2", 13);
        Student s3 = new Student(3, "jack3", 14);
        Student s4 = new Student(1, "jack1", 12);
        set.add(s1);
        set.add(s2);
        set.add(s3);
        set.add(s4);
        System.out.println(set);
        //无序:如果存放的实体类,会出现一个BUG  当某个实体类的属性完全一致,按理来说应该视为相同的值。但是set不会这么做。
        //解决办法:重写hashCode方法即可
        
        //hash:哈希  一种编码    每个对象都有一个独一无二的hash值  你就理解为键盘上的26个字母
//        Integer a = 123;
//        String b = "Nihao";
//        System.out.println(a.hashCode());
//        System.out.println(b.hashCode());
//        //存在的意义:  判断对象是否相等
        //一般来说判断对象是否相等都是通过==  但是==获取的内存地址 效率较低
        //所以hashCode效率较高、  hashCode相等,则进一步判断内存地址   如果hashCode都不能则内存地址肯定不等。
        
//        String a1 = new String("111");
//        String a2 = new String("111");
//        System.out.println(a1.hashCode());
//        System.out.println(a2.hashCode());
//        
//        Student s1 = new Student(1, "jack1", 12);
//        Student s2 = new Student(1, "jack1", 12);
//        System.out.println(s1.hashCode());
//        System.out.println(s2.hashCode());

6.9 IO流

       /*
         临时存储:变量,数组,集合等,都是存储在内存中,只要停止运行,这些数据都会消失。
         数据持久化:将数据保存在硬盘里面,只要不手动删除,则永久生效。
         
         常见的数据持久化技术:1.数据库       2.IO流
         
         数据库:通过Java代码将临时存储的数据存进数据库。
         IO流:通过Java代码将临时存储的数据存进指定的文件。 
         都是双向操作,能存也能取。
         
         注意:文件的相关操作都是导入  java.io 包
         
         
         构造方法如下:
         File  file  = new File(“要操作或要创建的文件夹或文件的路径”);
         
         常见方法如下:
         boolean  createNewFile();创建该文件,如果存在就不会创建
         
         boolean  mkdir();创建该文件夹,如果存在就不会创建
         
         
      */
      
//    案例1:在项目根目录下创建123.txt文件
//    File file = new File("123.txt");//这里是相对路径,表示项目的根目录
//    boolean b = file.createNewFile();
//    System.out.println(b);
      
//    案例2:在桌面下创建456.txt(注意路径里面的转义符)
      //如果想在非项目路径下创建文件,那么就需要用到绝对路径
      
//    File file = new File("C:\\Users\\Administrator\\Desktop\\456.txt");
//    boolean b = file.createNewFile();
//    System.out.println(b);
      
      
      //这里要注意路径的转义符   \是一个转义符。所以需要\\   来进行转义  以下写法是错误的。
//    File file = new File("C:\Users\Administrator\Desktop\456.txt");
      
      
      
//    案例3:在项目根目录下创建test文件夹,然后在内部再创建test.txt文件
      //注意:对文件的操作 只能是一层一层的来,不能够直接操作 多层目录。
//    File file = new File("test/test.txt");这种写法是错误的.
      
      
      //一般如果对多层的文件进行处理,一般都是将路径声明在外面。让它成为一个变量。
      String str = "test";
      File file = new File(str);
      file.mkdir();
      str = str + "/test.txt";
      file = new File(str);
      file.createNewFile();
//     File f = new File("123.txt");
//    boolean exists();返回文件或文件夹是否存在
//    System.out.println(f.exists());
      
//    String getAbsolutePath(); 返回文件或文件夹的绝对路径
//    String path = f.getAbsolutePath();
//    System.out.println(path);
      
//    boolean  delete(); 删除空文件夹或文件
//    boolean b = f.delete();
//    System.out.println(b);
      
//    isFile(); 判断是否为文件
//    boolean c = f.isFile();
//    System.out.println(c);
      
//    isDirectory();判断是否为文件夹
      
      
//    getName();返回file对象的文件名字
//    System.out.println(f.getName());


      
//    File[] listFiles();返回指定目录下的所有文件或文件夹,返回是file对象
      
      
      File file = new File("C:\\Users\\Administrator\\Desktop\\chap5");
      File[] listFiles = file.listFiles();
      for(int i = 0 ;i<listFiles.length;i++) {
         System.out.println(listFiles[i]);
      }
//     案例1:遍历指定目录下的所有文件夹以及文件。
      
//    File file = new File("C:\\Users\\Administrator\\Desktop\\chap5");
//    listFiles(file);
      
      
      
//    案例2:遍历指定目录下是否存在XXXX.xxx文件
      File file = new File("E:");
      String str = "魔性小人热身舞.mp4";
      findFile(file, str);
          
   }
    
   public static void listFiles(File file) {
      File[] listFiles = file.listFiles();
      for(int i = 0 ;i<listFiles.length;i++) {
         if(listFiles[i].isFile()){
            System.out.println( "文件名为:" + listFiles[i].getName());
         }else {
            System.out.println("文件夹名为:" +listFiles[i].getName());
            //下一步接着遍历
            listFiles(listFiles[i]);
         }
      }
   }
   public static void findFile(File file , String fileName) {
      File[] listFiles = file.listFiles();
      for(int i = 0 ;i<listFiles.length;i++) {
         if(listFiles[i].isFile()){
            System.out.println( "文件名为:" + listFiles[i].getName());
            if(listFiles[i].getName().equals(fileName)) {
               System.out.println("找到了!!" + listFiles[i].getAbsolutePath()  );
//             return;仅仅只能跳出当前方法,无法跳出之前递归的方法,
               System.exit(0);//直接结束JVM
            }
            
         }else {
            System.out.println("文件夹名为:" +listFiles[i].getName());
            //下一步接着遍历
            findFile(listFiles[i],fileName);
         }
      }
   }
    //别拿有用的文件夹删除,因为这是不可撤回的动作
   //删除指定文件夹:
   String str = "C:\\Users\\Administrator\\Desktop\\chap7";
   File file = new File(str);
   deleteDir(file);
   
   
}

public static void deleteDir(File file) {
   File[] files = file.listFiles();
   for(int i = 0;i<files.length;i++) {
      if(files[i].isFile()) {
         files[i].delete();
      }else {
         deleteDir(files[i]);
      }
   }
   file.delete();
}

        /*
            之前讲的File  仅仅只能对文件或文件夹进行删除或创建,无法对文件的内部进行修改。
            所以就需要额外学习以下知识点:    
                
            IO流全称为  :  input/output   stream
            
            input  :输入
            output :输出
            stream :流
            
            输入流:文件传输给内存,理解为读取文件(获取文件)
            输出流:内存传输给文件,理解为写入文件(修改文件)
            
            IO流的分类:
            按照流向分:
            输入流和输出流
            
            按照类型分:
            字节流和字符流
            
            字节流:按照字节为单位传输,适用于所有文件,例如图像,音乐,视频等。
            字符流:按照字符为单位传输,仅适用于文本类型文件,例如txt,word等
            
            所以共计4大流:字节输入流  字节输出流  字符输入流  字符输出流  
            
            字节   :计算机最小存储单位。      又称byte
            位    :计算机最小的单位,又称位    1byte = 8bit 
            字符   :1个字符 = 2个字节 = 16bit


            IO流的分类:
            字节输入流  字节输出流  字符输入流  字符输出流 
            
            IO流的种类分为差不多50多种,规律如下:
            
            字节输入流: 父类就是 InputStream   凡是:XxxInputStream 的都是字节输入流的实现类
            字节输出流: 父类就是 OutputStream  凡是:XxxOutputStream 的都是字节输出流的实现类
            字符输入流: 父类就是 Reader           凡是:XxxReader 的都是字符输入流的实现类
            字符输出流: 父类就是 Writer           凡是:XxxWriter 的都是字符输出流的实现类
            
        
        */
        
        /*
            字节输入流: 父类就是 InputStream   凡是:XxxInputStream 的都是字节输入流的实现类
            最常见的就是:FileInputStream
            
            构造方法如下:
            方法1: FileInputStream f = new FileInputStream("操作的文件的路径");
            方法2: FileInputStream f = new FileInputStream( new File("路径") );
            
        */
        
//        FileInputStream f = new FileInputStream("123.txt");
        
        //读取通过read方法  每次只能读取1个字节  返回的是int类型(ASCII码)  如果返回-1就表示读取结束
//        int i = f.read();
//        System.out.println(i);
        
//        byte b[] = new byte[100];//创建一个长度为100的数组
//        int k = f.read(b);//此时就表示每次都读取100字节。  i返回数组长度  数组的长度是随时改变的:表示读取了多少数据
//        //这个数组进入read方法之后,数组内容就会被填充的数据 填充
//        for(int i = 0 ;i<b.length;i++) {
//            System.out.println(b[i]);
//        }
//        
//        
//        f.close();//类似于Scanner;清除内存
        
        
        
        FileInputStream f = new FileInputStream("123.txt");
        //需求:将文本内容全部读取到控制台上。 每次读取10个字节
        byte b[] = new byte[10];
        while(true) {
            int k = f.read(b);
            if(k==-1) {
                break;
            }else {
                for(int i = 0 ;i<b.length;i++) {
                    System.out.print(b[i]);
                }
            }
        }
        f.close();

        /*
            字节输出流: 父类就是 OutputStream  凡是:XxxOutputStream 的都是字节输出流的实现类
            最常见的就是:FileOutputStream
            构造方法如下:
            方法1: FileOutputStream f = new FileOutputStream("操作的文件的路径");
            方法2: FileOutputStream f = new FileOutputStream( new File("路径") );
            
            //如果放了第二个参数 表示每次操作都是追加操作   如如果没放第二个参数或者放了false 就表示每次操作都是清空之后操作
            方法3: FileOutputStream f = new FileOutputStream("操作的文件的路径",true);
            方法4: FileOutputStream f = new FileOutputStream( new File("路径") ,true);
            
            
        */
        FileOutputStream f = new FileOutputStream("456.txt");
        //注意:每次重新执行,将内部内容清空再操作
        f.write(65);
        f.write(66);
        f.write(67);
        String str = "你好";
        byte[] bs = str.getBytes();
        f.write(bs);
        f.close();

        //将根路径下的123.txt复制成111.txt
        
        FileInputStream f1 = new FileInputStream("1.jpg");
        FileOutputStream f2 = new FileOutputStream("2.jpg");//如果文件不存在会自动帮你创建
        byte b[] = new byte[10];
        while(true) {
            int i = f1.read(b);
            if(i==-1) {
                System.out.println("操作完成");
                break;
            }else {
                f2.flush();
                f2.write(b);
            }
        }
        
        
        f1.close();
        f2.close();

        /*
            注意:字符流只能处理文本类型文件   如果处理非文本 将会失真 
            
            字符输入流: 父类就是 Reader           凡是:XxxReader 的都是字符输入流的实现类
            
            
            字符输出流: 父类就是 Writer           凡是:XxxWriter 的都是字符输出流的实现类
        */
        
        FileReader f1 = new FileReader("111.txt");
        FileWriter f2 = new FileWriter("333.txt");
        
        
        char c[] = new char[20];
        while(true) {
            int i = f1.read(c);
            if(i==-1) {
                break;
            }else {
                f2.flush();
                f2.write(c);
            }
        }
        
        f1.close();
        f2.close();

//        将a文件夹复制一份b文件夹,假设a文件夹中只有若干个图片。
        //文件的复制本质是复制文件 而非文件夹
        File f = new File("C:\\Users\\Administrator\\Desktop\\a");
        String path = "C:\\Users\\Administrator\\Desktop\\b";
        File file = new File(path);
        file.mkdir();
        
        File[] files = f.listFiles();
        for(int k = 0 ;k<files.length;k++) {
            
            FileInputStream f1 = new FileInputStream( files[k] );
            FileOutputStream f2 = new FileOutputStream( path + "\\" + files[k].getName() );
            byte b[] = new byte[110];
            while(true) {
                int i = f1.read(b);
                if(i==-1) {
                    System.out.println("复制完成");
                    break;
                }else {
                    f2.write(b);
                }
            }
            f1.close();
            f2.close();

6.10 多线程

1.进程的相关概念:每个独立运行的程序称为进程。例如QQ音乐。腾讯QQ。

2.线程的相关概念:每个进程又可以同时拥有多个线程。并且线程是可以同时执行的。一个进程最少拥有一个线程。
例如运行QQ音乐这个进程。它可以同时听歌,看视频,搜索歌曲。至少在执行3个线程。

当我们运行Java程序,就可以理解为开启了一个进程。
其中该进程下默认的一条进程就是main线程。

可以通过创建多个线程类,实现java的多线程。

如果没有使用多线程,代码的执行顺序就是从上到下依次执行。

例如:先听歌,再吃饭,再看电视,再聊天。如果是单线程,就只能一个一个按顺序执行。
如果是多线程:就可以一边听歌,一边吃饭,一边看电视,一边聊天。


多线程的应用场景:多用户同时使用的场景
例如:春节抢票 、  双十一购物 等 。高并发场景。

高并发:多个事件在同一个时间段发生。 例如同时吃饭和聊天。
高并行:多个事件在同一个时刻发生。 例如同时吃饭和听歌。

解决的最常见方式:利用多线程。
public class Test2 {
   //此时 main方法就可以看成一个线程。  此时一个单线程
   public static void main(String[] args) {
      
      /*
         与线程相关的类和方法都处于:  Java.lang.Thread下 所以无需导包。

         线程类的创建方法:(先有线程类。再有线程)
         1.继承Thread类
         
         2.重写run方法(线程的代码体都放在run方法里面)
         
         3.创建对象
         
         4.调用start方法


         备注:线程的执行权是随机的。
      
      
         由于Java是单继承,所以一般能不继承就不继承,把继承的机会让给更重要的类。
   
         所以一般都是使用下面这种方式来创建线程。
         
         1.实现runnable接口
         
         2.实现run方法(线程的代码体都放在run方法里面)
         
         3.创建上述类对象
         
         4.创建线程对象,然后将第三步的对象作为参数传递。

         5.调用start方法
      */
      
      
      A a = new A();
      a.start();
      
      B b = new B();
      Thread t = new Thread(b);
      t.start();
      
      for(int i = 1;i<=100;i++) {
         System.out.println("这是main线程" + i);
      }
   }
}



class  A extends Thread{
   public void run() {
      for(int i = 1;i<=100;i++) {
         System.out.println("这是A线程" + i);
      }
   }
}


class B implements Runnable{
   @Override
   public void run() {
      for(int i = 1;i<=100;i++) {
         System.out.println("这是B线程" + i);
      }
   }
   
}

public class Test3 {

    public static void main(String[] args) throws Exception {
      
//    Thread.currentThread() 获取当前线程
//
//    Thread.currentThread().setName() 给当前线程取一个别名
//
//    Thread.currentThread().getName() 获取当前线程的别名
//
//    Thread.sleep(long x); 让当前线程暂停x毫秒。让别的线程先执行。
//
//    isAlive();获取该线程是否活跃
      A1 a = new A1();
      Thread t = new Thread(a);
      t.start();
      
      
      System.out.println(Thread.currentThread().isAlive());//true
      System.out.println(111);
      Thread.sleep(3000);
      System.out.println(222);
      System.out.println(Thread.currentThread().isAlive());//true
      
      System.out.println("A1线程是否活跃" + t.isAlive());
   }
}

class A1 implements Runnable{

   @Override
   public void run() {
      System.out.println(111);
   }
   
}
public class Test4 {
   public static void main(String[] args) {
      /*
         CPU负责:执行线程。 那么存在多个线程,优先级是怎么样的?  随机的

         计算机:平均调度。理解为雨露均沾,每个线程都是平等的,都是同等的机会被CPU执行。
         
         
         Java:抢占时调度,理解为每个线程可以设置一个优先级。可以优先执行该线程。
         但是至于抢不抢的过,还是随机的。
         
         Java中每个线程默认优先级都是5,最大为10,最小为1;
         Thread.currentThread().setPriority(xxx);
         Thread.currentThread().getPriority(xxx);

      */
      
      Thread t = new Thread(new A2());
      
      t.setName("这是A2线程");
      Thread.currentThread().setName("main");
      t.setPriority(10);
      Thread.currentThread().setPriority(1);
      t.start();
      
      for(int i = 0 ;i<100;i++) {
         System.out.println(Thread.currentThread().getName() + i);
      }
      
   }
}

class A2 implements Runnable{

   @Override
   public void run() {
      for(int i = 0 ;i<100;i++) {
         System.out.println(Thread.currentThread().getName() + i);
      }
   }
   
}
线程的生命周期:

1.新建态:创建了一个线程还没有通过start方法开启。

2.就绪态:调用了start方法,但是还没有开始执行。   或阻塞态结束。

3.运行态:抢到了CPU资源,正在执行中。

4.阻塞态:在运行态的过程中,出现一点意外(例如:被别的线程抢去了cpu资源,调用了sleep,yield等方法)

5.消亡态:线程执行完毕。
public static void main(String[] args) {
   
   upload1();
}



public static void upload1() {
   //有会员的网盘下载
   for(int i = 1;i<=100;i++) {
      try {
         Thread.sleep(1000);
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      System.out.println("下载进度为:" + i + "%");
   }
}
public class Test7 {
   public static void main(String[] args) {
      /*
         创建一个类,该类有3个方法,分别是吃饭睡觉打豆豆。
         每个方法需要执行5秒。
      */
      
//    long t1 = System.currentTimeMillis();
//    a1();
//    a2();
//    a3();
//    long t2 = System.currentTimeMillis();
//    System.out.println(t2-t1);单线程执行时间约等于15秒
      
      long t11 = System.currentTimeMillis();
      Thread t1 = new Thread(new A4());
      Thread t2 = new Thread(new A5());
      Thread t3 = new Thread(new A6());
      t1.start();
      t2.start();
      t3.start();
      //下面的写法是不发正确计算结果的。因为多线程的特性,不会等t1t2t3执行完。而是main自己先执行完了
//    long t22 = System.currentTimeMillis();
//    System.out.println(t22-t11);
      

      
      //t1 t2 t3都要执行完毕才可以执行下面这行代码
      //t1 t2 t3 都处于死亡状态  isAlive返回值都要为false 才可以开始计算时间
      
      while(true) {
         if(t1.isAlive()==false && t2.isAlive()==false && t3.isAlive()==false ) {
            long t22 = System.currentTimeMillis();
            System.out.println(t22-t11);
            break;
         }
      }
   }
   
   public static void a1() {
      System.out.println("吃饭中");
      try {
         Thread.sleep(5000);
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      System.out.println("吃饭结束");
   }
   
   public static void a2() {
      System.out.println("睡觉中");
      try {
         Thread.sleep(5000);
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      System.out.println("睡觉结束");
   }
   
   public static void a3() {
      System.out.println("打豆豆中");
      try {
         Thread.sleep(5000);
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      System.out.println("打豆豆结束");
   }
}


class A4 implements Runnable{

   @Override
   public void run() {
      System.out.println("吃饭中");
      try {
         Thread.sleep(5000);
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      System.out.println("吃饭结束");
   }
}

class A5 implements Runnable{

   @Override
   public void run() {
      System.out.println("睡觉中");
      try {
         Thread.sleep(5000);
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      System.out.println("睡觉结束");
   }
}

class A6 implements Runnable{

   @Override
   public void run() {
      System.out.println("打豆豆中");
      try {
         Thread.sleep(5000);
      } catch (InterruptedException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
      System.out.println("打豆豆结束");
   }
}


public class Test8 {

   public static void main(String[] args) {
//    创建一个线程类,功能是卖票。初始为100张票。
//    然后定义三个线程对象,同时开启,理解为3个窗口同时卖票。
      
      I i = new I();
      Thread t1 = new Thread(i);
      Thread t2 = new Thread(i);
      Thread t3 = new Thread(i);
      t1.setName("窗口1:");
      t2.setName("窗口2:");
      t3.setName("窗口3:");
      t1.start();
      t2.start();
      t3.start();

      
      //该案例的写法是没有任何问题的  但是结果是不合人意的。
      //原因: 由于Java线程是抢占行   如果多个线程同时操作相同的变量 。会出现读写错误。
      //如何解决:见test9
      
      
   }
   
}
class I implements Runnable{
   
   int num = 100;//表示票的总数
   public void run(){
      
      while(true) {
         num--;
         if(num<0) {
            System.out.println("票卖完了");
            break;
         }else {
            System.out.println(Thread.currentThread().getName() + "卖了第" + (100-num) +  "张票");
         }
      }
         
   }
}
public class Test9 {

   public static void main(String[] args) {
//    创建一个线程类,功能是卖票。初始为100张票。
//    然后定义三个线程对象,同时开启,理解为3个窗口同时卖票。
      
      I1 i = new I1();
      Thread t1 = new Thread(i);
      Thread t2 = new Thread(i);
      Thread t3 = new Thread(i);
      t1.setName("窗口1:");
      t2.setName("窗口2:");
      t3.setName("窗口3:");
      t1.start();
      t2.start();
      t3.start();

      
      //该案例的写法是没有任何问题的  但是结果是不合人意的。
      //原因: 由于Java线程是抢占行   如果多个线程同时操作相同的变量 。会出现读写错误。
      //如何解决: 将操作相同变量的代码块 加上一个 锁机制。
      //锁机制: 只允许一个线程进入,并且只有当该线程执行结束之后,才允许别的线程进来。
      //语法:
      /*
         synchronized (this) {
            操作相同变量的语法代码
         }
         
      */
   }
   
}
class I1 implements Runnable{
   
   int num = 100;//表示票的总数
   public void run(){
      
      while(true) {
         try {
            Thread.sleep(20);
         } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         }
         synchronized (this) {//参数只需要保证是独一无二 都行
            num--;
            if(num<0) {
               System.out.println("票卖完了");
               break;
            }else {
               System.out.println(Thread.currentThread().getName() + "卖了第" + (100-num) +  "张票");
            }
            
         }
         
         
      }
         
   }
}
public class Test10 {
   public static void main(String[] args) {
      Money m = new Money();
      Thread t1 = new Thread(m);
      Thread t2 = new Thread(m);
      t1.setName("支付宝");
      t2.setName("微信");
      t1.start();
      t2.start();
      
   }

}

class Money implements Runnable{
   int money = 100;
   

   @Override
   public void run() {
      
      synchronized (this) {
         System.out.println(Thread.currentThread().getName() + "查询到余额为" + money);
         if (money >= 100) {
            System.out.println(Thread.currentThread().getName() + "开始取钱");
            money = money - 100;
            System.out.println(Thread.currentThread().getName() + "取钱成功");
         }else {
            System.out.println(Thread.currentThread().getName() + "取钱失败");
         }
      }
      
   }
   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值