命令行指令

使用WIN+R键显示页面,然后输入 cmd 进入控制台。

基础指令:

  Dir  :  列出当前目录下的文件及文件夹。 directory

        Md  :  创建目录    make directory

        Rd  :  删除目录     remove directory

        Cd  :  进入指定目录   come directory

       Cd ..  :  退回至上级目录

       Cd\  :  退回至跟根目录

       Del  :   删除文件   delete directory

       目录就是文件夹。

       删除一个类的文件时 del  *.文件类型

JDK、JRE、JVM三者关系 

    JDK (Java Development Kit  Java开发给工具集):包括Java的开发工具(编译工具:java.exe 打包工具:jar.exe)以及JRE.

     JRE(Java Runtime Environment   Java运行环境):包括了Java虚拟机以及Java程序所需的核心类库等,如果只想要运行开发好的Java程序,那么只需要安装JRE就可以。

     JVM(Java Virtual Machine): 实现了Java程序的跨平台性,对于不同的操作系统提供不同的Java虚拟机。

一、数据类型

如果输入类型不匹配的话,编译就会报错。---InputMisMatchException

引用数据类型变量存储的不是null就是地址值(含变量类型)。

1、整形:

Long类型初始化必须写L或者l(但是我没有写也对了,离谱???)

2、浮点型

3、字符类型

 在用命令行执行程序是,其默认用GBK来读取二进制文件,我们保存java文件时默认保存为UTF-8的二进制文件,所以当我们的代码中出现汉字时,就会出现乱码的情况。解决方法是,将我们的文件保存为ANS文件。

        数据容量(表示范围)大小关系:byte、short、char<short<int<long<float<double

等于号左边的任意两种类型做运算时,至少要用比最大类型高一级的类型去接收。

      强制类型转换:数据类型   变量名   =(转换类型)变量名;

Java用不同类型的变量去接收数据会报错:

所以需要强制类型转换 :

 Java中默认常量整数为int类型,常量小数为double 类型,也就是说一旦涉及到变量加上一个整数至少要用int去接收结果,变量加小数至少要用double去接收。

       而之前我所说的在定义long类型的变量并对其进行赋值操作时不写L和l也可以编译的原因是因为编译器将你所提供的数字看成了一个int类型的数字,然后默认做了自动类型提升(容量大的比变量接收容量小的变量的值),但是当你所提供的数字超过了int类型容量的时候,就会报错。例如:

 如果改变代码,加上L或者l就可以运行了:

在将浮点型数据赋值给整形数据时为截断取整。

4、String数据类型---字符串型

String数据类型为引用数据类型  S大写

另外获取字符串的某一位置上的字符:变量名+ . +charAt();

 str="01234";    char ch=str.charAt(pos)

该类型可以和其他八种基本数据类型做运算,但都是链接运算:+ 。例如:

当然连接运算的结果仍是String类型

5、进制

输出的时候以十进制的方式输出

二、算术运算符

1、杂记

java的算术运算符:+ 、- 、* 、/ 、 ++、 -- 、% 、& 、!=、 ==、&&、 &、|、||、^.

^:异或运算符,只要两边的不一样就是真的,一样为假。

short s1=10;

s1+=10; 编译成功。            s1=s1+10; 编译失败。short与常量10(int)的和要用Int类型的变量进行承接。    s1++;编译成功。

&&与& (||与|)虽然运算结果相同,但是他们是有区别的。&&与&的区别:
      当左边为真时,&&与&一样:

当左边情况为假时,&依旧会进行第二个条件的计算(num1++),而&&则直接结束,不进行num2++操作。所以&&被叫做短路与

类推,||与|的区别,||当左边条件为真时,直接结束运算,不进行num2++计算,而 | 当左边条件为真时,仍然进行num1++运算所以||被叫做短路或

开发中优先使用短路与和短路或。

2、位运算符(了解即可):

<<(左移运算符):左移几位就乘以2的几次方(空缺用0补齐)

       >>(右移运算符):右移几位除以2的几次方(补齐看最高位)

当然左移和右移都是有一定限度的。

懒得写了,自己看图。

已知:m^n^n=m,用该种方法可以交换两个数的值:

class myJava
{public static void main(String[] args){int num1=10,num2=20;num1=num1^num2;num2=num1^num2;num1=num1^num2;System.out.println("num1="+num1);System.out.println("num2="+num2);}
}

3、三元运算符(跟c++一样)

4、运算符的优先级

三、基本语法

1、从控制台读取输入(int、double、string)

import java.util.Scanner;//类似于c++的#include
class myJava
{public static void main(String[] args){Scanner scan=new Scanner(System.in);//创建一个类的对象System.out.print("请输入一个整形数字  :");int num=scan.nextInt();System.out.println("您输入的整形数字为 "+num);System.out.print("请输入一个字符串  :");String str=scan.next();System.out.println("您输入的字符串为 :"+str);System.out.print("请输入一个浮点型数字  :");double d1=scan.nextDouble();System.out.println("您输入的浮点型数字为 "+d1);}
}
/*构造一个Scanner对象,其传入参数为System.in 利用下列方法读取键盘数据:
nextLine( ); //读取一行文本,可带空格
next( ); //读取一个单词
nextInt( ); //读取一个int数值
nextDouble( ); //读取一个double数值
用hasNextInt()和hasNextDouble()检测是否还有表示int或double数值的字符序列 */

利用API查找  :

2、一维数组

数组是引用数据类型,引用类型的默认值为null(大致跟c++数组差不多)

数组名储存的是数组的第一个值的地址,数组名被储存在栈区,而数组里面的值储存在堆区、

(1)数组的声明以及初始化(可以写成和c++一样的,但是)

静态初始化:数组的初始化和数组的赋值操作分开进行

int[] arr;//数组的声明
arr =new int [] {1,2,2,4,9};
int arr1[];//单独写一个这也不错
​

动态初始化:数组的初始化和赋值操作同时进行

int[] arr2=new int[] {1,2,3,4,5};

注意,以下语法是错误的

int[2] arr3=new int[9];

int[] arr3=new int[];

int arr[];      arr={1,2,3};

(2)获取数组长度

arr.length;       (没有括号)

(3)数组的默认初始化的值

整形:0

浮点型:0.0

char :  0  (不是‘0’)

String :null(表示该字符串为空,不等同于“null”

boolean : false ;

package first_package;public class first {public static void main(String[] args) {int[] arr;arr =new int [] {1,2,2,4,9};System.out.println("整型");for(int i=0;i<arr.length;i++)System.out.println(arr[i]);System.out.println("******************");double[] arr1=new double[4];System.out.println("浮点型");for(int i=0;i<arr1.length;i++)System.out.println(arr1[i]);System.out.println("******************");char[] arr2=new char[6];System.out.println("char");for(int i=0;i<arr2.length;i++)if(arr2[i]==0)System.out.println(0);System.out.println("******************");String[] arr3=new String[5];System.out.println("String ");for(int i=0;i<arr3.length;i++)if(arr2[i]==0)System.out.println(arr3[i]);}}

运行结果如下:

(4)创建确定长度数组

    int arr[];arr=new int[100];System.out.println(arr.length);//100

3、二维数组

跟一维数组一样。

但是跟c++不一样,要写行数,不必写列数,然后给每行确定有多少个元素:

像这样:

int arr[][]=new int [3][];for(int i=0;i<3;i++){arr[i]=new int[i+1];for(int j=1;j<=i+1;j++){arr[i][j-1]=j;}}for(int i=0;i<3;i++){for(int j=0;j<=i;j++)System.out.print(arr[i][j]);System.out.println();}

 或者写成 int[] arr4[]=....也可以

获取二维数组的行数: arr.length;               

获取二维某一行的列数: arr[0].length;

(1)默认初始化值

int arr[][]=new int [3][3];

内层初始化值(arr[0]):地址

外层初始化值:与一维数组初始化值想同

int arr[][]=new int [3][];

内层初始化值(arr[0]):null (此时指针指向的值为空,所以没有地址或者二维数组的元素是一维数组,数组是引用类型,所以默认初始化为空)

外层初始化值:不能调用,会报错

      int arr1[]=new int[]{1,2,3,4,5};
      int arr2[]=arr1;     

       等同于起别名,他们地址相同,指向了同一个值。

           4、对数组的操作(Arrays类)

                   首先在文件中包括Arrays类:import java.util.Arrays;

                   返回值类型  函数名 参数列表             

                (1) 判断两数组是否相同(包括顺序):

                                 boolean   equals(int a[],int b[]) 

                  (2)输出数组信息

                                String toString (int a[])

                ​​​​​​  (3) 将指定值填充入数组 (完全替换为同一个数 val)

                                void fill(int a[],int val)

                (4)对数组进行排序

                                void sort(int[] a)

                  (5) 对序后的数组进行二分法检索指定的值

                        ​​​​​    int binarySerath(int []a,int key);

package first_package;import java.util.Arrays;//包括Arrays类public class first
{public static void main(String[] args) {int arr[]= {1,9,4,7,2,3,8,5,6};int arr1[]= {1,5,7,8,4,2,3,6,9};Arrays.sort(arr);//数组排序System.out.print(Arrays.toString(arr));//数组输出System.out.println();boolean Isequals=Arrays.equals(arr1, arr);//比较数组是否相等System.out.println(Isequals);int pos=Arrays.binarySearch(arr, 8);//二分法查找System.out.println(pos);Arrays.fill(arr, 8);//填充数System.out.print(Arrays.toString(arr));}
}

四、面向对象(上)

        面向过程强调的是功能行为,以函数为最小单位,考虑怎么做。

        面向对象将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。

        面向对象更加强调运用人类在日常的逻辑思维中采用的思想方法和原则。如抽象、分类、继承、聚合、多态等。

         面向对象的三大特征:封装、继承、多态。

       1、基础知识

            (1)设计类

                       设计类就是设计类的成员。

  属性=成员变量=field=域、字段

 方法=成员方法=函数=method

例如设计Person类:

public class opp001 {public static void main(String[] args){Person one=new Person();one.name="Tom";System.out.println(one.name);one.talk();Person two=one;System.out.println(two.name);two.name="hh";System.out.println(one.name);}
}
class Person {String name;public void talk(){System.out.println("HELLO WORLD!");}
}

创建示例:Person one=new Person();

     如果: Person two=one;  那么在one和two指向的是同一个堆区的地址。

(2)、内存解析

         堆(heap):此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都是在这里分配内存。这一点在JAVA虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。

栈(Stack):虚拟机栈。虚拟机栈用于存储局部变量(main方法中的也是)等。局部变量表存放了编译器可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完后,自动释放。

方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

       也就是说,对象的首地址存在栈区,但是对象的成员存放在堆区。

        定义成员方法: 权限修饰符 +返回值类型+方法名+(参数列表)+ { 方法体 } 

(3)匿名对象

匿名对象:创建了一个对象,但是没有显形的赋值的对象,即为匿名对象(匿名对象也是存在堆区)。

特征:匿名对象只能调用一次(每个匿名对象都不相同)

package opp001;public class opp001 {public static void main(String[] args){new Person().talk();new Person().run();showPerson s=new showPerson();s.show(new Person());}
}
class showPerson{public void show(Person p){p.talk();p.run();}
}
class Person {String name;public void talk(){System.out.println("HELLO WORLD!");}public void run () {System.out.println("run");}
}

2、方法

(1)构造并使用类方法(函数)

java没有函数,只有存在于对象的方法

 方法一可以调用它本身,以及同处于同一类下的其他方法。(但是main方法是静态的,所以mian只能调用它所在的类的静态方法)

   第一种:   所以我们要把方法(函数)写进类里面,然在用到方法是需要先构造类的对象,然后通过对象来实现功能,而且类主函数可以写进不同的文件里面(看方法的权限

示例:

两个都在文件opp001包下的文件:Array.java (写入类)

ArrayText.java (写入主函数)

第一个文件代码如下:

package opp001;//写入类public class Array {public void showArr(int arr[]){for(int i=0;i<arr.length;i++){System.out.print(arr[i]+"  ");}}
}

第二个文件代码如下:

package opp001;public class ArrayText {public static void main(String[] args){int arr[]= {1,2,3,4};Array array=new Array();//构造Array类的对象array.showArr(arr);    //利用对象完成功能}}

第二种:将方法直接写进main方法所在的类中,如果是非静态的就构造与main方法所在类的对象,然后使用;如果是静态的可以通过“类名.”的方式直接使用。

public class methodUse {public static void main(String[] args) {methodUse s=new methodUse();s.show();}public void show() {System.out.println("????");}
}

(2)方法的重载

同一个类中,允许存在一个以上的同名方法。(和c++一样:返回值不能决定重载

  方法名和参数列表可以确定一个方法。

(3)可变个数形参的方法

        a.可变个数形参的形式:数据类型 + ... (三个点,无空格)+变量名

                 public void show(String ... str)

        b.可调用可变个数形参的方法是,传入的参数个数可以是:0,1,2......

        c.可变个数形参的方法与本类中方法名相同、形参不同的方法之间构成重载

          

但是,当传进的参数为不可变个数的形参时,优先选择不可变个数形参。

  d.可变个数形参的方法有本类中方法名相同,形参类型也相同的数组之间不构成重载。(两者不能同时存在)

 e.可变个数形参在方法的形参列表中,必须声明在末尾

 f.可变个数形参在方法的形参中,最多只能声明一个可变形参

g.可变个数形参的参数可以当做数组使用

(4)变量的赋值

如果是基本数据类型的话,那么赋值就是传递变量中储存的数据值,

如果是引用数据类型,那么赋值就是传递值所在的 地址。

(5)方法中的参数传递(跟c++一样)

    如果是基本数据类型的话,那么传递就是传递变量中储存的数据值,

如果是引用数据类型,那么传递就是传递值所在的地址。

2、封装与隐藏

(1)封装性的体现

a  :将成员变量私有化,然后提供公共的方法(Set、Get...)来操作该成员变量,而不能直接对该变量进行操作。(跟c++类似)

b :方法的私有化,例如在Arrays类中的每一个方法都需要一个交换两个位置上的值得操作,该操作就是一种方法,但是该方法只能在Arrays类中使用

c  :单例模式。。。。

(2)权限修饰符

                  a.Java规定的4中权限(从小到大排列):private、缺省(啥也不写)、protected 、public

                          b、4种权限可以用来修饰类及类的内部结构、属性、方法、构造器、内部类

                          c、具体的,四种权限都可以用来修饰类的内部结构、属性、方法、构造器、内部类。修饰类的话,只能使用缺省、public

                          d. 不同包的非子类只有public 可用

(3)封装性总结

                     Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。

3、构造器(和c++相似)

Java的构造器要对每一个对象赋值,即便是空值也需要赋值。

在继承中,父类一般都要有一个空构造器,以便对父类中的属性赋值。(否则就会报错)

(1)构造器的作用

a.   创建对象

b.   初始化对象的信息

(2)构造器的特点

                      a.如果没有显式的服能够以构造器的话,则系统默认提供一个空参的构造器。

                      b.定义构造器的格式:权限修饰符 +类名+(形参列表)

                      c.一个类中定义的多个构造器,彼此之间形成重载

                      d.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器

                      e.一个类中,至少会有一个构造器

4、Javabean

 JavaBean是一种Java语言写成的可重用组件

              所谓JavaBean,是指复合如下标准的Java类 :

                        a.类是公共的

                        b.有一个无参的公共的构造器

                        c.有属性,且有对应的get和set方法

5、this 

      当方法或者构造器的参数名与类的属性名相同时,必须使用this来表明哪一个变量是否为属性(因为就近原则)。

     使用方法如下: this.属性名    this.方法名

       另外还可以通过this+(参数列表)的方式来调用构造器。

例如:

另外,this+(参数列表)必须写在构造器的最开始,而且每个构造器最多只能用一个其他构造器,器不能造成死循环。

6、package(包)

(1)为了更好的实现项目中的类的管理,提供包的概念、

(2)使用package声明类或接口所属的包,声明在源文件的首行。

(3)包,属于标识符,遵循标识符的命名规则,规范,见名知意。

(4) 命名的时候,每“ .”一次,就代表一层文件目录。

         补充: 同一个包下,不能命名同名的接口、类。不同的包下可以。

  7、inport(导入)

 (1)在源文件中使用import显式的导入指定包下的类或接口

例如使用Arrays类是需要导入该包(编译器自动导入,或者鼠标放在上面,然后手动导入):

 (2)声明在包的声明和类的声明之间。

(3)如果需要导入多个类或接口,那么久并列显显式多个inport语句即可(跟c++类似)

(4)举例,可以使用“java.util.*”的方式,一次性导入util包下的所有的类或接口

比如Scanner和Arrays同属一个util包,那么我们可以直接导入util包:

import java.util.*;

相当于:

import java.util.Arrays;
import java.util.Scanner;

(5)如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略import语句。例如:String类和System

(6)如果在代码中使用不同包下的同名的类,那么就需要使用,类的全类名的方式指明调用的是 哪个类。

比如在两个包里面都有Dog类,当我们需要在另一个类(importText)里面需要使用的时候那么一个可以导入,另一个就必须写全名(包名+“.”+类名);

package opp001;import java.util.*;
/*import java.util.Arrays;
import java.util.Scanner;*/import bao.Dog;public class importText {public static void main(String[] args) {Dog d=new Dog(1);opp00.Dog d1=new opp00.Dog();
}
}

 (7)如果已经导入java.a包下的类,但是如果需要使用a包下的子包的类的话,仍需要导入(可以理解为子包与父包是并列关系)

(8)import static组合的使用,调用指定类或接口下的静态的属性或方法

8、Eclipse快捷键

Shift + 回车  ---> 在该行下面再创建一个空白行

Ctrl +Shift + 回车--->在该行上面创建一个空白行

           Ctrl --->查看源码   或者Ctrl+Shift+t 搜索需要查看的类

Ctrl + D---->选中行,然后删除行

Ctrl +Shit + F--->格式化代码(变整齐)

Tab---->整体后移

Shift+Tab---->整体前移

Alt+/---->单行注释   Alt+\---->取消单行注释

Ctrl+Shift+X--->变成大写

Ctrl+Shift+Y 把当前选中的文本全部变为小写

           Ctrl+O --->快速显示 OutLine

Ctrl+T --->快速显示当前类的继承结构

Ctrl+Shift+F--->调整格式

五、面向对象(中)

    9、继承(extends)

 继承的好处:a. 减少了代码的冗余,提高了代码的复用性

                                       b.便于功能的扩展

                                       c.为之后的多态性的使用提供了前提

继承的格式: class A extends B    {      };

A: 子类、派生类、subclass    B:父类、超类、基类、superclass

 继承的特点:

(1)一旦子类A继承父类B以后,子类A就获取了父类B中声明的所有的属性和方法。(包括private权限的属性和方法)

(2)子类继承父类以后,还可以声明自己特有的属性和方法,实现功能的扩展,因此,子类和父类的关系不同于子集和集合的关系

Java关于继承的规定:

(1)一个类可以被多个子类继承。

(2)Java中类是单继承性,一个类只能有一个父类

(3)子类和父类只是相对而言

(4)子类直接继承的父类成为直接父类,间接继承的父类称为间接父类

(5)子类继承父类以后,就获取了直接父类以及间接父类中声明的属性和方法

一般类和Object类的关系:

(1)如果没有显式的声明一个类的父类的话,则此类继承于java.lang.Object

(2) 所有的类(除了java.lang.Object类之外)都直接或者间接的继承于java,lang,Object类

(3)基于(2),所有的类都具有java.lang.Object类所声明的功能。

10、方法的重写

重写:子类继承父类以后,可以对父类同名同 参数的方法进行覆盖操作。

应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法是,实际上执行的是子类重写父类的方法。

重写的规定

声明的方法:权限修饰符 返回值类型 方法名(参数列表) (throws暂时不清楚)异常的类型{     方法体     }

约定俗称,子类中的叫重的方法,父类中的叫被重写的方法。

(1) 子类重写的方法的方法名誉形参列表与父类被重写的方法的方法名和参数列表一样

(2)子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符,但是,子类不能重写父类中声明为private权限的方法(后者该操作不能叫重写)

(3)返回值类型:

a.父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能void

b.父类被重写的方法的返回值类型是A类时,则子类重写的方法的返回值类型可以是A类或者A类的子类

c.父类重写的方法的返回值类型是基本数据类型时,子类重写的方法的方绘制类型只能是跟父类一样的类型

(4)子类重写的方法抛出的异常不能大与父类被重的方法抛出的异常

子类和父类同名同参数的方法要么都声明为static类型(不是重写),或者都不声明为static类型(考虑重写)

11、super

 super语句只能在类实例构造函数内部使用(和this相同)

(1)super可用理解为:父类的

(2) super可以用来调用属性、方法、构造器

(3)super的使用(表明父类的作用域)

a.我们可以在子类的方法或者构造器中,通过使用“super.属性”或“super.方法”的方式,显式地调用父类中声明的属性或方式。但是,通常情况下,我们习惯的省略"super.”

b.特殊情况,当子类和父类中定义了同名的属性或者方法是,我们想在子类中调用父类中的属性或者方法时,使用"super."表明调用的是父类中声明的属性 或方法

(4)super和构造器

a.我们可以在子类的构造器中显式的使用“super(形参列表)”的方式调用父类中声明的制定的构造器。

b.“super(形参列表)”的使用必须声明在子类构造器的首行。 无论通过哪个构造器创建子类对象,需要保证优先初始化父类。当子类继承父类后,“继承”父类中所有的属性和方法,因此子类有必要知道父类如何对对象进行初始化。

c.在我们类的构造器中,针对“this(形参列表)”和“super(形参列表)”只能二选一,不能同时出现

d.在构造器的首行,如果没有显示的声明“this(形参列表)”和“super(形参列表)”,则默认的调用父类的空参构造器。如果没有空参构造器,那么就需要显式的调用父类的其他构造器。(一般父类都需要保留空参构造器)

创建对象时,会先自动调用父类的构造函数,再自动调用子类的构造函数,也就是说我们需要在子类的构造器里面包括父类的构造器。 前面我们已经知道在我们定义了一个父类,然后在我们用子类继承父类的时候,我们必须要在子类的构造器开头调用父类的构造器,如果我们不写父类构造器,那么编译器会给我们提供一个默认的空参构造器,那么在我们写子类的构造函数时,编译器会自动默认的帮我们写入super();来调用父类的默认的空参构造器。但是当我们写了父类的构造器时,那么就没有系统提供的默认构造器,也就是说我们需要自己通过super(参数列表)的方式来自己调用父类的构造器。(收藏里面写的好)

12、多态性

(1)理解多态性,可以理解为一个事物的多种形态。

(2)何为多态性:父类的引用指向子类的对象(或者之类的对象赋值给父类的引用)

(3)多态的使用 :虚拟方法调用

有了对象的多态性之后,我们在编译期,只能调用父类中声明且子类中的重写的的方法,但在运行期,我们实际执行的是子类重写的父类的方法。也就是编译 看左边,运行看右边。

(4)多态性的使用前提 :继承和方法的重写

(5)多态性不涉及属性,也就是说如果父类跟子类有相同类型且同名的属性时,利用多态操作的是父类的属性,而不是子类的属性

多态的细节补充:

        子类继承父类之后,在创建多态的对象之后,我理解为创建了一个拥有被子类重写了方法的父类的对象,只能调用父类中有的东西,不能调用子类中所独有的属性和方法,但是通过new创建的对象在堆区实际上是有子类独有的属性和方法的。如果需要调用子类的方法可以使用强制类型转换。例如:

       Man extends Person;

Person p=new Man;

Man m=(Man)p;

13、重写和重载的区别

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法名称做修饰。对于编译器而言,这些他同名方法就成了不同的方法。 他们的调用地址在编译期就确定了。

Java的重载是可以包括父类的子类的,即子类可以重载父类的同名不同参数的方法。所以对于重载而言,载方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或者“静态绑定”。

而对于多态,只有等到方法调用的那一刻,解释运行器才可以知道所要调用的具体的方法,这称为“晚绑定”或“动态绑定”

14、instanceof

在使用强制类型转化的时候需要一定的条件,也就是说对象需要是类的子类,那么instanceof关键字可以帮助我们来进行判断是否可以转换。

 Man extends Person;   Women extends Person ;  Person extends Creature

package duotai;public class PersonText {
public static void main(String[] args) {Person p=new Man();if(p instanceof Creature){System.out.println("Creatrue");}if(p instanceof Person){System.out.println("Person");}if(p instanceof Man){System.out.println("Man");}if(p instanceof Women){System.out.println("Women");}}
}

运行结果:

也就是说,只要是父类都可以匹配成功

15、 ==和equals()方法

(1) ==

a. ==可以用于基本数据类型和引用数据类型

b. ==比较基本数据类型是,比较两个变量保存的值是否相同(会有自动类型提升)

Int  i=10; double b=10.0;    b==a //true

c.==比较的是引用数据类型时,比较的是变量的地址,即两个引用是否是一个实体

d。在使用 == 符号是,必须保证符号两边的变量类型一致

(2)equals()方法

a. equals()是一个方法

b.只使用于引用数据类型

c.Object类中关于equals()方法的定义在为重写时仅仅判断两个变量的地址值是否相同

public boolean equals(Object obj){return(this==obj);}

d.String .Date.File、包装类等都重写了equals()方法。重写以后比较的不是地址值,而是两个对象的属性是否相同

e.重写equals()方法

因为equals()方法是Object类里面有的,而每一个类都继承了Object类的属性和方法,所以需要重写,重写后的方法比较的是两个对象的每一个地址、是否是空对象指向、所属类、对象的每个属性

(3)getclass()方法

         对象.getclass()   获取对象所属的类

import java.util.Objects;public class Man extends Person{String major;String name;int age;public void eat(){System.out.println("学生吃饭");}@Overridepublic boolean equals(Object obj) {if (this == obj)//判断地址是否相同return true;if (obj == null)//判断是否是空指向return false;if (getClass() != obj.getClass())//判断是否是同一类return false;Man other = (Man) obj;//转换为类型转化return age == other.age && Objects.equals(major, other.major) && Objects.equals(name, other.name);}
}

  toString()方法也是一样的,除了String,FIle,Date几个类不用重写外,其余需要我们重重写。

16、包装类

也就是将每个数据类型变为一个类,是已经规定好的类。

 Byte、Short、Integer、Long、Float、Double、Boolean、Character

将基本数据类型转化为包装类:

​​       int num = 20;Integer in1=new Integer(num);System.out.println(in1); Integer in2=new Integer("123");System.out.println(++in2); Boolean b=new Boolean("true");System.out.println(b);Boolean b1=new Boolean("True");System.out.println(b1);Double d1=new Double(2.2);System.out.println(d1);Float f=new Float(2.2f);

新特性:

        int num3=4;Integer in5=num3;System.out.println(in5);int num4=20;class_one c=new class_one();c.print(num4);public void print(Object obj){System.out.println(obj.toString());}

将包装类转化为基本数据类型:

Integer in3=new Integer(20);int num1=in3.intValue();System.out.println(num1);Boolean b2=new Boolean("true");boolean b4=b2.booleanValue();System.out.println(b4);

新特性之后直接转化(自动拆包):

        Integer in4=new Integer(10);int num2=in4;

 新特效:自动拆包和自动装包

在调用参数是包装类的方法时,可以用基本数据类型来调用,赋值时更加的方便。

基本数据类型转化为String :

        int num4=20;String str=num4+"";System.out.println(str);String str2=String.valueOf(num3);System.out.println(str2);

String转化为基本数据类型:

方法一:通过包装类的构造器和自动拆包

    转换为数字类型的时候,字符串里面只能有数字。

        int num6=new Integer(str3);System.out.println(num6);

方法二:通过调用包装类的parseXxx(String)方法

        int num7=Integer.parseInt(str3);System.out.println(num7);

 在Boolean类转换的时候,不分大小写,只要字符串是‘true’就会返回true,只要不是。就会返回false

包装类和基本数据类型之间可以直接运算!!!

        Integer in1=10;
        Integer in2=5;
        int num=9;
        System.out.println(in1-num);

一些小问题:


 (1)  Object ojb=true?new Integer(1):new Double(2.0);
             System.out.println(ojb);

    结果输出1.0 ,因为三目运算符要求后面两个的数据类型一样,所以他自己做了自动类型提升,将int 转化为了double

(2)   Integer in1=new Integer(1);
            Integer in2=new Integer(1);
            System.out.println(in1==in2);//false
            Integer in3=1;
            Integer in4=1;
            System.out.println(in3==in4);//true

             Integer内部定义了IntegerCache结构。IntegerCashew中定义了Integer[],保存了从-128~127范围的整数,如果我们使用自动装箱的方式,给Integer赋值的范围在-128~127内,那么我们使用的是类中定义好的对象,因此,两个对象的地址值一样。
            Integer in5=128;
            Integer in6=128;
            System.out.println(in5==in6);//false

六、面向对象(下)

17 、static

(1)static:静态的

(2)static可以用来修饰属性、方法、代码块、内部类

(3)使用static修饰属性:静态变量(或类变量)

        a.按是否使用了static分为:静态属性和非静态属性(实例变量)

                实例变量:创建了对象A和B,A和B都独立的拥有该类中定义的非静态属性,修改A的非静态属性不会对B的非静态属性造成改变

                静态变量:创建了对象A和B,A和B共同拥有该类的静态属性,修改A的静态属性会对B的静态属性造成改变,或者说类的静态属性只有一份,静态属性属于类,属于类的每一个对象,而不是只属于某一对象。

        b.可以通过”类名.静态属性”来调用静态变量,也可以通过“对象.静态属性”的方法来调用静态属性。

(4)使用static修饰方法:静态方法

        a.静态方法中只能调用静态方法或属性,非静态方法中既能使用非静态方法和属性,也能使用静态方法和属性。

        b.其余的和静态变量一样

关于a的解释:在运行的时候,加载类的时候,先加载的是类的信息和类的静态结构(在方法区的静态域中),而甚至有在创建了对象实例之后才有了非静态结构。因此静态方法先于非静态结构出现,因此a.

(5)static补充

        在静态的方法中不能使用this关键字、super关键字。

        在调用非静态结构时省略的其实是“类名.”,而不是“this.”

18、单例的设计模式(只有一个对象)

设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模免去我们自己再思考和摸索。式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,”套路”、
                所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生-一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无,法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。---(尚硅谷宋红康老师)

方法一(饿汉式)//直接创建对象

好处:加载时间过长(在内存中一直存在)

坏处:线程安全

class Home{
    //1、创建私有构造器,使得不能无限制的创建对象
    private Home() {
        
    }
    //2、构造一个对象
    //4、使创建的对象也为静态的,静态方法才可以返回创建的静态对象
    private static Home jia=new Home();
    //3、创建一个方法,返回创建的对象,是方法为静态方法(就可以在不创建对象的情况下调用)
    public static Home make()//如果静态方法是private,那么不能在main方法中通过类名的方式调用
    {
        return jia;
    }
}

方法二(懒汉式):不用不创建,用了才创建

class order {private order() {}private static order o1=null;public static order make(){if(o1==null)o1=new order();return o1;}
}

好处:延迟对象的创建

坏处:线程不安全

OK 让我们来解决懒汉式的线程不安全问题,用synchronized将需要同步的代码包含起来:

package About_Thread;本程序旨在将懒汉式的单例模式变为线程安全的class Bank{将构造器私有化,使得没有默认的构造器,也无法大量的创建对象private Bank(){}创建一个私有的静态的类对象private static Bank instance=null;方法一:效率较低-->因为可能有多个线程等待进入同步代码块public static Bank getInstance(){synchronized(Bank.class){if(instance==null){instance =new Bank();}}return instance;}方法二:效率比第一种方法要高,因为避免了其余线程进入同步代码块,就是将synchronized及其所包含的同步代码块用if条件语句包起来public static Bank getInstance(){if(instance==null) {synchronized (Bank.class) {instance = new Bank();}}return instance;}
}

19、代码块

(1)代码块的作用:出初始化:初始化类、对象

(2)只能用static修饰代码块,或者不修饰

(3)代码块有两类:静态代码块  非静态代码块

(4)静态代码块

a.内部可以有输出语句

b.随着类的加载而执行,并且只执行一次

c.如果一个类中定义了c多个静态代码块,那么按照声明的先后顺序执行(正常人谁会写一个以上)

d.静态代码块先于非静态代码块的执行

e.静态代码块只能调用静态结构,不能调用非静态结构

(5)非静态代码块

a.内部可以有输出语句

b.随着对象的创建而执行,每new一个对象就执行一次

c.如果一个类中定义了多个非静态代码块,那么按照声明的先后顺序执行(正常人谁会写一个以上)

d.非静态代码块中可以调用静态和非静态的结构

示例:

package luogu.src.August;class Creature {Creature(){System.out.println("Creature的构造器!");}static {System.out.println("Creature的静态代码!");}{System.out.println("Creature的非静态代码块!");}
}
class Person extends Creature{Person(){super();System.out.println("Person的构造器!");}static {System.out.println("Perosn的静态代码块");
}{System.out.println("Person的非静态代码块!");}
}
public class text extends Person{static {System.out.println("text的静态代码块!");}public static void main(String[] args) {System.out.println("--------------------------");new Person();System.out.println("--------------------------");new Person();}
}

由于text继承了Person,所以在text的静态main方法调用时,需要先加载Person、Creature和text三个类的信息,所以在加载的时候执行了静态代码块(由父及子,静态先行),执行完三者的static代码块之后,创建爱Person对象,然后会先调用父类Creature的非静态代码块以及构造器(代码块先于构造器),然后调用子类Person的非静态代码块和构造器(由父及子,代码块先行)。

  总体顺序就是:先执行完所有类的静态代码块(只执行一次),然后按照类的顺心,分别执行每个类的非静态代码块和构造器(可执行多次)。

执行结果如下:

20、final

(1) final可以用来修饰的结构:类、方法、变量

(2)final用来修饰一个类,那么该类不可以被其他类所继承(断子绝孙),但是该类里面的属性是可以改变的

(3)final用来修饰方法:那么该方法不可以被重写

(4)final用来修饰变量

a.final用来修饰属性:那么可以通过显式初始化、代码块中初始化、构造器中初始化的方式来对属性赋值,我的理解就是只要不能对同一个对象进行二次使用的方式,那么就可以赋值。

public class key_final {final int num1=0;//显式赋值final int num2;final int num3;static final int num4;final int num5;//代码块赋值static{     //静态代码块只能操作静态结构num4=0;     }//非静态代码块不能为static、final修饰的num4赋值,因为//非静态代码块可以执行多次,且num4只有一个且是只能赋值一次 {num5=0;}//构造赋值(每个构造器都要为没有显示赋值且没有在代码块中//赋值的final修饰的属性赋值public key_final(int num){num2=num;num3=0;}public key_final(int n1,int n2){this(n1);}public static void main(String[] args) {key_final k=new key_final(1,2);System.out.println(k.num3);}
}

(5)补充、

a.static和final共同修饰的变量为全局常量

b.静态代码块只能操作静态结构,非静态代码块不能为static、final修饰的static和final共同修饰的常量赋值,因为非静态代码块可以执行多次,且该常量只有一个且是只能赋值一次

c.构造赋值:每个构造器都要为没有显示赋值且没有在代码块中赋值的final修饰的属性赋值

21、abstract

(1) abstract :抽象的

(2)abstract可以修饰的结构:类、方法

(3)abstract修饰类。抽象类

        a.该类不能初始化

        b.抽象类一定有构造器,便于子类实例化时调用(创建子类对象时会先调用父类的构造器来初始化父类的属性,然后才初始化子类特有的属性)。

        c.开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作。

 (4)abstract修饰方法:抽象方法

        a.抽象方法只有方法的声明,没有方法体(无法调用,需要可以实例化的子类重写)。

        b.包含抽象方法的类,一定是一个抽象类。反之,抽象中也可以没有抽象方法。

        c,若子类重写了父类中的所有的抽象方法后,子类可以实例化;如果子类没有重写父类中所有的抽象方法,那么子类也是抽象类,也不能实例化,也需要abstract修饰。

 补充:

d.abstract不能用来修饰:属性、构造器

  e.abstract不能用来修饰私有方法:子类不能重写父类中声明为private权限的方法(后者该操作不能叫重写)。

        f.abstract不能用来修饰静态方法:  子类和父类同名同参数的方法要么都声明为static类型(不是重写),或者都不声明为static类型(考虑重写)。

        g.abstract不能修饰final 修饰的方法和类:因为final修饰的类不能被继承,final修饰的方法不能被重写,而abstract就是需要子类继承或者重写的。

抽象类的匿名子类的对象抽象类的匿名对象的匿名子类

public class abstractText1 {public static void main(String[] args) {//创建抽象类的匿名对象的子类Person p=new Person() {//需要重写抽象方法public void eat(){System.out.println("抽象类的匿名子类对象吃饭"); }};method(p);System.out.println("*************************");//创建抽象类的匿名对象的匿名子类method(new Person() {//也需要重写抽象方法public void eat(){System.out.println("抽象类的匿名子类的匿名对象吃饭");}});
}public static void method(Person p){p.eat();p.walk();}
}

抽象类Person:

public abstract class Person {int age;String name;public Person(int age, String name) {super();this.age = age;this.name = name;}public Person() {}public abstract void eat();public void walk(){System.out.println("人走路");}
}

22、接口

(1)接口的关键字:interface

(2)Java中,接口和类是并列的属性(表现为都可以被子类(或实现类)继承(或实现))

(3)如何定义接口:定义接口中的成员

        定义全局常量:public static final 变量类型 变量名   可以省略为--->变量类型 变量名

        抽象方法:public  abstract 返回值类型 方法名  可以省略为--->返回值类型 方法

        静态方法:

        默认方法:

 (4)接口没有构造器,也就是说接口不能实例化

(5)Java开发中,接口通过类区实现(implements)的方式来使用

         如果实现类覆盖了接口中的所有的抽象方法,则此实现类就可以实例化,反之则不能

    比如接口: interface Flyable{ },用类Plane实现该接口:class Plane implements Flyable{  }

(6)Java类可以是实现多个接口  ---->弥补了Java单继承性的局限性

        格式:class A extend B implement C,D,E....

(7)接口与接口之间可以继承,而且可以多继承

        interface F extends D,E.....

(8)接口的具体使用,可以体现多态性

        a.接口可以作为方法的参数(接口也体现多态性)  

(9)接口是一种规范 

(10)实现类无法改变从接口中继承的属性的值,因为他们是全局常量(static final)

public class InterfaceText {public static void main(String[] args) {System.out.println(Flyable.MAX_SPEED);System.out.println(Plane.MAX_SPEED);
//      Plane.method(new Plane());}//接口做方法的参数public static void method(Flyable f){f.fly();}
}
interface Flyable {public static final int MAX_SPEED=10;int MIN_SPEED=1;public abstract void fly();void stop();
}
//类实现接口
class Plane implements Flyable{//类覆盖接口的方法@Overridepublic void fly() {System.out.println("飞机要起飞了!");       }@Overridepublic void stop() {System.out.println("飞机要降落了!");      }
}

   关于接口的匿名:

public class USBText {public static void main(String[] args) {//创建非匿名子类的非匿名对象Phone phone = new Phone();System.out.println("创建非匿名子类的非匿名对象");Phone p=new Phone();method(p);//创建非匿名子类的匿名对象System.out.println("创建非匿名子类的匿名对象");method(new Phone());//创建匿名子类的非匿名对象System.out.println("创建匿名子类的非匿名对象");USB u=new USB(){@Overridepublic void start() {System.out.println("电脑开始数据传输!");             }@Overridepublic void end() {System.out.println("电脑结束数据传输!");             }           };method(u);System.out.println("创建匿名子类的匿名对象");method(new USB() {@Overridepublic void start() {System.out.println("MP3结束数据传输!");}@Overridepublic void end() {System.out.println("MP3结束数据传输!");             }           });
}static void method(USB b){b.start();b.end();}
}
interface USB{void start();void end();
}class Phone implements USB{@Overridepublic void start() {System.out.println("手机开始数据传输!");        }@Overridepublic void end() {System.out.println("手机结束数据传输!");     }
}

代理模式:

package Interface;public class daili {public static void main(String[] args) {Proxyserver p=new Proxyserver(new Server());p.browse();}
}
interface NetWork{public void browse();
}
class Server implements NetWork{@Overridepublic void browse() {System.out.println("连接真实的网络!");    }
}
class Proxyserver implements NetWork{private NetWork work;public Proxyserver(NetWork work) {this.work=work;}public void check() {System.out.println("连接前的检查工作");}@Overridepublic void browse() {        check();work.browse();}
}

关于接口的静态方法和默认方法

        静态方法的创建:public static void method1() {  方法体 }

默认方法的创建:public default void method2() { 方法体 } 或者省略public:

default void method3(){  方法体 } 但是权限不变

(1)静态方法不能通过实现类的对象调用,但是可以通过接口直接调用

(2)子类可以重写接口中的默认方法

(3)当父类和接口中有同名同参数的方法时,子类(或实现类)在没有重写的情况下默认执行父类的方法---类优先原则

(4)默认方法可以通过实现类的对象调用

(5)当继承的多个接口中存在同名同参数的默认方法时

        解决方法1:实现类需要重写该方法

        解决方法二:指明是谁的方法

                在实现类中重写该方法,然后利用  接口名.super.方法名  的方法来调用某接口的默认方法。如 Compare1.super.method5(); 表明实现类的method5是接口Compare1的默认方法

package Interface;public class add1 {public static void main(String[] args) {Subclass s=new Subclass();//method1(静态方法)不能通过实现类的对象调用//The method method1() is undefined for the type Subclass//s.method1();//但是可以通过接口直接调用Compare1.method1();//子类可以重写接口中的默认方法s.method2();//当父类和接口中有同名同参数的方法时,//子类(或实现类)在没有重写的情况下默认执行父类的方法---类优先原则s.method3();//默认方法可以通过实现类的对象调用s.method4();s.method5();}}
interface Compare1{public static void method1() {System.out.println("接口:静态方法1");}public default void method2() {System.out.println("接口:默认方法2");}//public可以省略,但是权限仍是publicdefault void method3(){System.out.println("接口:默认方法3");}default void method4(){System.out.println("接口:默认方法4");}default void method5() {System.out.println("1的方法");}
}
class Subclass extends Superclass implements Compare1,Compare2{//overrides Interface.Compare1.method2public void method2(){System.out.println("实现类重写:默认方法2");System.out.print("调用接口的默认方法:");Compare1.super.method2();}//当继承的多个接口中存在同名同参数的默认方法时//解决方法1:实现类需要重写该方法
//  @Override
//  public void method5() {
//      System.out.println("实现类重写method5");
//  }//解决方法二:指明是谁的方法@Overridepublic void method5() {// TODO Auto-generated method stubCompare1.super.method5();//表明实现类的method5是接口Compare1的默认方法}
}
class Superclass {public void method4(){System.out.println("父类的方法4");}
}
interface Compare2{default void method5() {System.out.println("2的方法");}
}

运行结果如下:

23、内部类

(1)Java重允许将一个类A声明在另一个类B,则类A就是内部类,类B称为外部类

(2)内部类的分类:成员内部类(静态,非静态)、局部内部类(方法内、代码块内、构造器)

(3)成员内部类

        作为外部类的成员: a、调用外部类的结构(静态只能调用静态)

                                          b.可以被static修饰

                                          c.可以被四种权限类型修饰 

         作为一个类:a.类内可以定义属性、方法、构造器

                               b.可以被static修饰

                               c.可以被static修饰

(4)需要注意的问题

a.如何实例化成员内部类的对象

    //利用外部类对象区创建非静态内部类对象,
    Person p=new Person();
    Person.Dog dog=p.new Dog();

外部类名.内部类名 外部类对象名 = 外部类对象名.new 内部类名();

需要使用外部类名来表明调用的是谁的内部类。

//创建静态内部类对象,需要时刻标注内部类的外部类,
    Person.Cat cat=new Person.Cat();

b.如何在内部类中区分调用外部类的结构

         Person.this.name;  外部类名.this.结构名

package InnerClass;public class InnerClassText1 {public static void main(String[] args) {//创建非静态内部类对象Person p=new Person();Person.Dog dog=p.new Dog();dog.run();dog.print();//创建静态内部类对象,需要时刻标注内部类的外部类Person.Cat cat=new Person.Cat();cat.run();}
}
class Person{String name="Tom";public void run(){System.out.println("人在跑");}class Dog{String name="旺财";public void run(){System.out.println("人养的狗在跑");}public void print(){Person.this.run();}//区分内部类和外部类同名的属性 外部类名.this属性名public void setName(String name){System.out.println(name);System.out.println(this.name);System.out.println(Person.this.name);}}public static class Cat{String name="狗砸";public void run(){System.out.println("人养的小猫在跑");//    Person.this.run();静态不能调用非静态结构}}
}

运行结果:

七、异常

1、异常概述与异常体系结构

异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)
        ●Java程序在执行过程中所发生的异常事件可分为两类:
                 ➢Error: Java虚拟机无法解决的严重问题。如: JVM系统内 部错误、资源耗尽等严重情况。 比如: StackOverflowError(栈溢出:死循环,自身调用自身)和OOM(堆溢出)。 一般不编写针对性的代码进行处理。
                 ➢Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如: 
                         a.空指针访问
                         b.试图读取不存在的文件
                         c.网络连接中断
                         d.数组角标越界

             

2、常见异常

          异常可以分为编译时异常(受检异常)和运行时异常(非受检异常)

编译时异常(checked):

                IOException

                        FileNotFoundException

                ClassNotFoundException

运行时异常(unchecked):

                  NullPointerException--->空指针异常

                  ArrayIndexOutofBoundsException--->数组角表越界异常

                  ClassCastException--->类型转化异常

                  NumberFormatException--->数字转化异常

                  InputMissmatchExcepption--->输入不匹配异常

                  ArithmeticException--->数字计算异常(除以0)

3、异常处理

 抓抛模型:

过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码出生成一个对应异常的队象(每一种异常都规定为一种类),并将此对象抛出,一旦抛出异常对象以后,其后面的代码就不再执行

关于异常对象的产生:①系统自动产生的异常对象

                                                     ②手动生成一个异常对象,并throw(抛出)

过程二:“抓”:可以理解为异常的处理方式:

                方法①:try-catch_finally

                方法②:throws     

4、try-catch-finally异常处理机制

        注意:

               a.try-catch中定义的变量属于局部变量,不能在{}之外使用

                b.catch条件中父类需要写在子类后面,不能让子类没有执行的可能,不然会报错

                c.finally中是一定会执行的代码,也就是说不管有没有返回值,finally中的代码会在return前执行;不管程序有没有错误,finally中的代码也会执行。

                d.异常处理的两种方法:①String getMessage()         ②void printStackTrace()

                e.try_catch结构可以嵌套

                f.如果一个try语句中有两个以上的异常,那么相当于只有第一个异常,因为执行的时候第一个异常后的代码都不执行

        try{

        //会出现异常的代码}catch(异常类1 变量名){

        异常的解决方法}catch(异常类2 变量名){

        异常的解决方法}....

finally{

        需要一定执行的代码}

public class text1 {public static void main(String[] args) {String str="abc";try {int num=Integer.parseInt(str);System.out.println("hello1");}//父类需要写在子类后面,不能让子类没有执行的可能,不然会报错
//      catch(Exception e)
//      {
//
//      }catch(NullPointerException e){System.out.println("出现空指针异常");System.out.println(e.getMessage());}catch(NumberFormatException e){System.out.println("出现数据转化异常");System.out.println(e.getMessage());e.printStackTrace();}//必要执行的代码放在finally中finally{System.out.println("一定会执行的代码");}System.out.println("Hello2");//try-catch中定义的变量属于局部变量,不能在{}之外使用//System.out.println(num);}}

finally的代码一定实现,即使经过多层的调用!!!

public class Text3 {public static void main(String[] args) {method3();}public static void method1() throws Exception{try {System.out.println("进入方法1");throw new Exception() ;}finally {System.out.println("一定会执行的代码1");}}public static void method2() throws Exception{try {System.out.println("进入方法2");method1();}finally{System.out.println("一定会执行的代码2");}}public static void method3() {try{method2();}catch(Exception e) {System.out.println("进入方法3");}finally {System.out.println("一定会执行的代码3");}}
}

 体会:

        使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错,相当于我们使用try-catch-finally将一个编译时可能出现的异常延迟到运行时出现。

           开发中,由于运行时 异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了,针对编译时异常,我们一定要考虑异常的处理。

5、throws异常处理机制

 a、“throws+异常类型”写在方法的声明处,指明此方法执行时可能会抛出的异常类型。一旦方法体执行是,出现异常,仍会在异常代码出生成一个异常类的对象,然后该异常对象被抛出。异常代码后续的代码就不会再执行。另外,throws关键字对外声明的是该方法有可能发生异常,并不能指定抛出此异常,也就是说抛出的throws只能说可能,但是不能说一定会抛出,出现其他的异常也一样会被paochu

package yichang;public class throwsText {public static void main(String[] args) throws NumberFormatException{method2();}public static void method2() {try {method1();}catch(NumberFormatException e) {System.out.println("数值转化异常!");}}//通过throws 把错误向上抛由方法的调用者解决,直至抛给虚拟机,然后死机public static void method1() throws NumberFormatException{String str="abc1";int a=Integer.parseInt(str);System.out.println("不会输出的语句");}
}

b、try-catch-finally:真正的将异常解决了。throws的方法只是将异常抛出给方法的调用者。没有真正将异常处理掉。

c、如果将异常传递给main方法运行还是会出错

d、但是如果throws的异常和出现的异常不一样也是可以解决的,只要try-catch语句中有真正出现的异常的类型。例如:

public class throwsText {public static void main(String[] args) {method2();}public static void method2() {try {method1();}catch(NumberFormatException e) {System.out.println("数值转化异常!");}}//通过throws 把错误向上抛由方法的调用者解决,直至抛给虚拟机,然后死机public static void method1() throws ArithmeticException{String str="abc1";int a=Integer.parseInt(str);System.out.println("不会输出的语句");}
}

离谱,而且那个NumberFormatException也不是NullPointerException的子类:

好了,让离谱的问题不再离谱:

        throws关键字对外声明的是该方法有可能发生异常,并不能指定抛出此异常,也就是说throws只能说可能,但是不能说一定会抛出,出现其他的异常也一样会被抛出。

当代码运行出现错误的时候,

(小知识复习:选中类然后Ctrl+T查看他的族谱,哈哈哈)

可能每一个throws后面都默认跟了一个空。

        什么都不抛出就是最小的异常类。

e、之前提到过子类抛出的异常不能比父类要大,因为需要用到多态,所以解决父类的异常的try-catch也必须可以解决子类抛出的异常。

代码中NullPointerException是RuntimeException的子类

public class Extends_yichang {public static void main(String[] args) {Extends_yichang.method1(new Son());}public static void method1(Father f) {try {f.text();}catch(RuntimeException e) {System.out.println("运行异常");}}
}
class Father{public void text() throws RuntimeException {}
}
class Son extends Father {public void text() throws NullPointerException{System.out.println(10/0);System.out.println("???");}
}

6、手动抛出异常对象

             通过throw关键字手动的抛出一个异常,该异常不是由系统抛出,而是由我们通过

 语法:throw + new + 异常类()   手动抛出,然后就跟系统本身产生的异常的解决方法一样

public class ThrowText {public static void main(String[] args) {Student s=new Student();try {s.setId(-10);} catch (Exception e) {// TODO Auto-generated catch blockSystem.out.println(e.getMessage());}}
}
class Student{int id;public void setId(int id) throws Exception {if(id>0) {this.id=id;}else {//生成异常对象并throw(抛出)throw new Exception("输入的数据有误!");}}
}

输出结果如下(正常运行):

7、自定义异常

 自定义异常的要求:

        a.继承现有的异常结构:RuntimException、Exception

        b.提供全局常量:serialVersionUID,该常量作为一个序号版本号,来确定一个类(身份证号)

        c.编写两个构造器,一个空参,一个有参。

先来看看Exception类的定义:

​​​​public class Exception extends Throwable {//类的序列static final long serialVersionUID = -33875169124229948L;//空参构造器public Exception() {super();}//有参构造器public Exception(String message) {super(message);} //然后后面就各种构造器
}

所以我们只要模仿Exception类就行了:

public class MyException extends Exception{//序列号static final long serialVersionUID = -33875169934229948L;//空参构造器public MyException(){}//有参构造器public MyException(String str){super(str);}
}

throws和throw 的区别:

        throw是异常处理的机制,而throw则是抛出异常的方法之一。

        throws在方法声明处,而throw在方法体中

八、多线程

1、基本概念

程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指段静态的代码,静态对象
        ●进程(process)是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生、存在和消亡的过程。--生命周期
                ➢如:运行中的QQ,运行中的MP3播放器
                ➢程序是静态的,进程是动态的
                ➢进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
        ●线程(thread),进程可进一步细化为线程, 是一个程序内部的一条执行路径
                ➢若一个进程同一时间并行执行多个线程,就是支持多线程的
                ➢线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
                ➢一个进程中的多个线程共享相同的内存单元/内存地址空间-->它们从同一个堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。

        每个进程都有自己独立的:虚拟机栈、程序计数器

        多个线程共享一个进程中的结构:方法区,堆 

并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
        并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。

一个Java应用程序java.exe, 其实至少有三个线程: main()主线程,garabage_collector()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

 多线程程序的优点:

a.提高应用程序的相应。对图形化界面更有意义,可以增强用户体验

b.提高计算机系统CPU的利用率

c.改善程序结构。将既长又复杂的进程,分为多个进程,独立运行,利于理解和改善

2、多线程的创建(一)

a.创建继承于Thread类的类

        b.重写run()方法,并将想要在多线程运行的代码放入run()方法中

        c.创建该类的对象

        d.通过该类的对象调用start()方法

        e.start()方法的作用是启动当前线程,并且调用当前线程的run()方法

        f.一个对象只能调用一次start()方法,不能多次调用,如果想要创建两个以上的线程的话需要创建多个Thread子类的对象

        g.Thread.currentThread().getName()方法用于查看线程的名字

查看start()的代码:

 public synchronized void start() {if (threadStatus != 0)throw new IllegalThreadStateException();//.....
}

在第一次调用之后,threadStatus的值会发生改变,第二次调用就会抛出异常

public class Thread_text1 {public static void main(String[] args) {//3、创建Thread的子类的对象Mythread1 m=new Mythread1();//4、调用对象的start方法m.start();//一个对象只能创建一个进程(也就是说只能调用一次start()方法//如果需要创建多个进程,那就需要创建多个Thread子类的对象//m.start();错误//MyThread m1=new MyThread();//正确//m1.start();for(int i=0;i<10;i++) {System.out.println(Thread.currentThread().getName()+" :"+i);}}
}//1、继承Thread类
class Mythread1 extends Thread{//2、重写run方法@Overridepublic void run() {for(int i=0;i<10;i++)System.out.println(Thread.currentThread().getName()+" :"+i);}
}

运行结果如下(出现交互证明是并发的多线程):

也可以通过匿名子类的方法来创建多线程

public class noname {public static void main(String[] args) {new Thread(){@Overridepublic void run() {System.out.println("通过匿名子类及匿名对象创建线程");}}.start();}
}

不同的子类创建的线程会形成交互,也是并行的多线程,也会形成交互:

package Thread_knowledge;* 该程序旨在证明有Thread的类创建的线程之间也是多线程,他们彼此之间也会形成交互public class demo1 {public static void main(String[] args) {MyThread1 m1=new MyThread1();MyThread2 m2=new MyThread2();m1.start();m2.start();}}
class MyThread1 extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(getName()+":"+i);}}
}
class MyThread2 extends  Thread{@Overridepublic void run() {for (int i = 10; i < 20; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}
}

输出结果如下:

3、线程的常用方法

①start():启动当前线程,并调用run()方法

        ②run():     将需要在线程中执行的操作的代码放在run()方法的方法体中(子类需要重写该方法)

        ③currentThread():返回当前的线程

                 public static native Thread currentThread();

        ④getName():  返回当前线程的名字

                 public final String getName()

        ⑤setName():  设置线程的名字

        ⑥yield(): 释放CPU的占据执行权,也就是cpu会去停止执该线程a,并且会在a,b,c...多个线程中选择一个线程去执行,并且仍可能会选择原来的线程

(IDEA:Alt + Shift + z:选择添加分支语句)

    ⑦join(): 停止正在执行的线程,而去执行调用该方法的线程,直到调该方法的线程执行完毕。假如在执行线程a,此时线程b调用了该方法,那么会停止执行线程啊,而去执行线程b,直到线程b执行完毕。该方法会抛出一个异常。

                public final void join() throws InterruptedException { }

        ⑧stop():强机结束该线程

        ⑨isAlive():  判断当前线程是否结束

                public final native bollean isAlive();

        ⑩sleep(long millis ): 会使当前线程阻塞millis毫秒,在阻塞后CPU可以执行其他的线程。

join()示例(在main线程执行到i=15,转去执行Thread-0线程,直至Thread-0线程执行完毕):

public class demo1_method {public static void main(String[] args) {MyThread1 m=new MyThread1();m.start();for (int i = 10; i <20 ; i++) {if(i==15) {try {m.join();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print(Thread.currentThread().getName()+":"+i+"  ");}}
}
class MyThread1 extends Thread {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.print(Thread.currentThread().getName()+":"+i+"  ");}}
}

执行结果:

(Ctrl + Shift + / --->多行注释)

4、优先级

 线程的优先级决定了线程被cpu执行的概率的大小,而不是先将优先级高的执行完然后执行执行优先级小的(ps:不会吧,不会吧,不会真的有人只看到看后半截吧)

public static final int MIN_PRIORITY=1;

        public static final int MIN_PRIORITY=5;

        public static final int MIN_PRIORITY=10;

setPriority(int Priority): 用来设置优先级

public final void setPriority(int newPriority);

getPriority(): 用来得到线程的优先级

                public final int getPriority();

public class Priority {public static void main(String[] args) {优先级的大小只能影响CPU选择执行线程的啃可能性,而不是先将优先级大的进程,再执行优先级小的进程优先级的数值public static final int MIN_PRIORITY = 1;public static final int NORM_PRIORITY = 5;  默认优先级public static final int MAX_PRIORITY = 10;//设置main的优先级Thread.currentThread().setPriority(Thread.MAX_PRIORITY);MyPriority2 m=new MyPriority2();//设置m的优先级m.setPriority(Thread.MIN_PRIORITY);m.start();for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+"&"+Thread.currentThread().getPriority());}}
}
class MyPriority2 extends Thread{@Overridepublic void run() {for(int i=0;i<10;i++){System.out.println(getName()+"&"+getPriority());}}
}

5、多线程的创建(二)

a.利用实现Runnnable接口的方法来创建线程

  1、创建一个实现Runnable接口的类

                2、实现run()方法,将需要在线程中实现的代码放在run()方法之中

                3、创建实现Runnable接口的类的对象

                4、创建参数为实现Runnable接口的类的对象

                5、调用Thread类的对象的start()方法,实际调用的是实现了Runnable接口的类的run()方法

注意:同一个实现了Runnable接口的类的对象可以多次作为Thread构造器的实参,也就是说可以实现多个接口共享一个对象的数据(ps:666)

代码测试如下:


*   本程序旨在记录并尝试用实现Runnable的方法去创建一个线程//1、创建一个实现Runnable接口的类
class Run1 implements Runnable{//2、实现run()方法,将需要在线程中实现的代码放在run()方法之中@Overridepublic void run() {for (int i = 0; i < 10; i++) {//因为该类不是Thread类的子类,所以不能直接调用getName()方法System.out.println(Thread.currentThread().getName()+" :"+i);}}
}
public class RunnableText1 {public static void main(String[] args) {//3、创建实现Runnable接口的类的对象Run1 r=new Run1();//4、创建参数为实现Runnable接口的类的对象Thread t=new Thread(r);t.setName("线程一");//5、调用Thread类的对象的start()方法,实际调用的是实现了Runnable接口的类的run()方法t.start();//同一个实现了Runnable接口的类的对象可以多次作为Thread构造器的实参Thread t1=new Thread(r);t1.setName("线程二");t1.start();}
}

b.两种创建线程的方法的比较

在我们的开发中优先选择实现Runnable接口的方法。

原因:1、实现的方式没有类的单继承的局限性

2、实现的方式更适合用来处理多个线程共享数据的情况

联系: public class Thread implements Runnable    也就是说Thread也实现了Runnable接口

相同点:两种方式都需要重写run()方法,将线程要执行的逻辑声明在run()中

6、解决线程的安全问题

本程序旨在实现Runnable接口的方式写一个卖票的小程序,但是仍然存在线程的安全问题

  1、问题:在卖票的过程中出现了重票、错票-->出现了线程的安全问题

2、问题出现的原因:当某个线程操作车票的时候,尚未完成操作时,其他线程参与进来,也操作车票,导致出现重票以及错票的问题

本程序如下:

public class Window1_Runnable {public static void main(String[] args) {Window_sell w=new Window_sell();Thread t1=new Thread(w);Thread t2=new Thread(w);Thread t3=new Thread(w);//因为三个对象用的是用一个参数,所以他们的tickets也是同一个t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}
class Window_sell implements Runnable{//服了,我也不想把tickets设为1000,但是设为100的话就只有两个或者一个窗口会卖票了,可能我运气比较差了public  int tickets=1000;@Overridepublic void run() {while(true){if(tickets>0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+tickets);tickets--;}else{break;}}}
}

运行结果如下(分别出现了重票和错票的问题):

3、解决方法:当一个线程t1在操作线程时,其他线程不能参与进来,直到t1完成对tickets的操作时,其他线程才可以开始操作tickets,这种情况即使t1出现了阻塞,也不会出现线程的安全问题
4、在java中,我们通过同步机制来解决线程的安全问题

Synchronized 作用范围 :

        作用于方法时,锁住的是对象的实例(this);

        当作用于静态方法时,锁住的是Class实例,又因为Class的相关数据存储在永久带PermGen
(jdk1.8 则是 metaspace),永久带是全局共享的,因此静态方法锁相当于类的一个全局锁,
会锁所有调用该方法的线程; 

        synchronized 作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。它有多个队列,
        当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中

( 该段文字摘自其他的大佬,上链接

版权声明:本文为CSDN博主「Mark-Wang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wanghuan1990519wha/article/details/106887769)

方法一同步代码块:

synchronized(同步监视器){
      需要被同步的代码
  }

  说明:
      a.操作共享数据的代码,即为需要被同步的代码
      b.共享数据:多个线程共同操作的变量,比如本代码中的tickets就是共享数据
      c.同步监视器,俗称:锁。任何一个类的对象都可以当做锁。(ps:直接用this难道不香吗)
        要求:多个线程必须要共用同一把锁

实现Runnbale接口的代码如下

package About_Thread;本程序旨在实现Runnable接口的方式写一个卖票的小程序,但是仍然存在线程的安全问题1、问题:在卖票的过程中出现了重票、错票-->出现了线程的安全问题2、问题出现的原因:当某个线程操作车票的时候,尚未完成操作时,其他线程参与进来,也操作车票,导致出现重票以及错票的问题3、解决方法:当一个线程t1在操作线程是,其他线程不能参与进来,知道t1完成对tickets的操作时,其他线程才可以开始操作tickets,这种情况即使t1出现了阻塞也不会出现线程的安全问题4、在java中,我们通过同步机制来解决线程的安全问题方法一:synchronized(同步监视器){需要被同步的代码}说明:a.操作共享数据的代码,即为需要被同步的代码b.共享数据:多个线程共同操作的变量,比如本代码中的tickets就是共享数据c.同步监视器,俗称:锁。任何一个类的对象都可以当做锁。(ps:直接用this香吗)要求:多个线程必须要共用同一把锁public class Window1_Runnable {public static void main(String[] args) {Window_sell w=new Window_sell();Thread t1=new Thread(w);Thread t2=new Thread(w);Thread t3=new Thread(w);//因为三个对象用的是用一个参数,所以他们的tickets也是同一个t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}
class Window_sell implements Runnable{//服了,我也不想把tickets设为1000,但是设为100的话就只有两个或者一个窗口会卖票了,可能我运气比较差了public  int tickets=1000;@Overridepublic void run() {while(true){synchronized (this){if(tickets>0){/*try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}*/System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+tickets);tickets--;}else{break;}}}}
}

输出结果就是不会出现重票以及错票问题(ps:好家伙!这不等于没说,但是大哥,1000行咋截屏。。。)

针对继承Thread类的线程如下(ps:好多代码都是重复的,但是俺就是想写上,哈哈哈):

   需要创建一个静态类对象作为各个对象共同的锁。或者直接使用类名.class的方式也可以。

package About_Thread;*  该程序旨在解决三个窗口一共买一百张票的问题,但是让存在线程不安全的问题,有待解决
*  问题已解决public class Window {public static void main(String[] args) {Sell_tickets s1=new Sell_tickets();Sell_tickets s2=new Sell_tickets();Sell_tickets s3=new Sell_tickets();s1.setName("窗口1");s2.setName("窗口2");s3.setName("窗口3");s1.start();s2.start();s3.start();}
}
class Sell_tickets extends Thread {public static int tickets=1000;//创建一个静态属性类作为锁,这样不同的对象的锁就是一个类对象public static Object obj=new Object();@Overridepublic void run() {while(true) {可以使用obj(唯一的对象)来作为锁synchronized (obj) {同样可以使用类名.class的方式作为锁,也就是说类也是一个对象,牛哇此处的synchronized不能包含太少的代码,包含太少的代码可能还会出现线程安全的问题,包含太多的代码可能会出现错误比如将while(true)包含进来,那么第一个被执行线程会执行到将票卖完,然后其他窗口就不能卖票。当然,
在现实世界是不能出现这种情况的synchronized (Sell_tickets.class) {if (tickets > 0) {/*try {sleep(100);} catch (InterruptedException e) {e.printStackTrace();}*/System.out.println(getName() + "卖票,票号为:" + tickets);tickets--;} else {break;}}}}
}

方法二 同步方法

将需要同步的代码封装进一个synchronized修饰方法之中,然后通过run()方法调用该方法。

 关于同步方法:

        1、同步方法仍然涉及到同步监视器,只是不需要我们显式的声明

        2、非静态的同步方法,同步监视器是:this,静态的同步方法的同步监视器是:当前类本身,也就是类.class,因为静态方法之中无法使用this关键字

方法二:同步方法权限 + synchronized + 返回值类型 +方法名(参数列表){需要被同步的代码}此方法相当于方法中的代码全包含在synchronized中,然后通过run()调用该方法public class Window1_Runnable {public static void main(String[] args) {Window_sell w=new Window_sell();Thread t1=new Thread(w);Thread t2=new Thread(w);Thread t3=new Thread(w);//因为三个对象用的是用一个参数,所以他们的tickets也是同一个t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}
class Window_sell implements Runnable{//服了,我也不想把tickets设为1000,但是设为100的话就只有两个或者一个窗口会卖票了,
//可能我运气比较差了public  int tickets=1000;@Overridepublic void run() {while(true){//通过run()调用方法show();if(tickets<=0){break;}}}//将需要包含在synchronized中的代码封装在一个方法之中public synchronized void show(){if(tickets>0){//diaoyogn/*try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}*/System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+tickets);tickets--;}}
}

7、死锁

死锁

*         不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁 。(或者说两个人线程互相拿着对方的锁)

*          出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

*          举例:就好像两个线程一起吃饭,两个线程都各自拿着一根筷子,它们都在等着对方把筷子给自己,所以两个线程都陷入了阻塞状态,谁也无法运行,两败俱伤...

* 解决方法

*          专门的解决方案原则

*          尽量减少同步资源的定义

*          尽量避免嵌套同步(synchronized中的synchronized)

死锁举例

package lock;* 死锁:
*   不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
*   出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
*   举例:就好像两个线程一起吃饭,两个线程都各自拿着一根筷子,它们都在等着对方把筷子给自己,所以两个线程都陷入了阻塞状态,谁也无法运行,两败俱伤...
* 解决方法:
*   专门的解决方案、原则
*   尽量减少同步资源的定义
*   尽量避免嵌套同步(synchronized中的synchronized)
*
*public class Hard_lock {public static void main(String[] args) {//StringBuffer str1=new StringBuffer();StringBuffer str2=new StringBuffer();new Thread(){@Overridepublic void run() {//1、线程1拿到str1锁synchronized (str1) {str1.append('a');str2.append('1');//2、线程1陷入阻塞状态,转去执行线程2try {sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//5、从线程2中拿来执行权,但是无法打开str2锁,因为str2锁在线程2手中,cpu转去执行线程2synchronized (str2) {str1.append('b');str2.append('2');System.out.println(str1);System.out.println(str2);}}}}.start();new Thread(new Runnable(){@Overridepublic void run() {//3、线程2拿到str2的锁synchronized (str2) {str1.append('c');str2.append('3');//4、线程2陷入阻塞状态,转去执行线程1try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//6、从线程1中拿来执行权,但是无法打开str1锁,因为str1锁在线程1手中,cpu转去执行线程1...进入死循环synchronized (str1) {str1.append('d');str2.append('4');System.out.println(str1);System.out.println(str2);}}}}).start();}
}

死锁的另一种情况

package lock;* 本程序旨在表现死锁的一种情况
*  我所标的顺序只是一种可能的情况,线程都可能先执行import About_Thread.text;public class text1 implements Runnable {A a=new A();B b=new B();//6、主线程调用a.A_method(b)public void init(){Thread.currentThread().setName("主线程");a.A_method(b);System.out.println("进入了主线程之后");}@Overridepublic void run() {Thread.currentThread().setName("副线程");//2、run()方法在副线程中运行,调用b的B_method()方法b.B_method(a);System.out.println("进入了副线程之后");}//main:我在这!!!public static void main(String[] args) {text1 t1=new text1();//1、通过start()调用实现接口的类的run()方法new Thread(t1).start();//5、执行主线程,调用init()方法t1.init();System.out.println("程序执行完毕");}
}
class A {//7、主线程拿到锁a(同步方法的锁就是它的对象)public synchronized void A_method(B b){//synchronized修饰的同步方法的锁为当前的对象a,System.out.println("当前线程名:"+Thread.currentThread().getName()+" 进入");//ps:糟糕,又忘记了咋搞出来try-catch 真是笨蛋 ---> 选中代码 Ctrl + Alt + T//8、主线程陷入睡眠,转去执行副线程try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("当前线程名: "+Thread.currentThread().getName()+ "企图调用B实例的last方法");//10、主线程想要执行b.last()方法就需要锁b,但是此时锁b在副线程手中,主线程无法执行,转去执行副线程。然后,无限套娃开始了b.last();}public synchronized void last(){System.out.println("进入了A类的last方法内部");}
}
class B{//3、副线程拿到锁b(同步方法的锁就是它的对象)public synchronized void B_method(A a){System.out.println("当前线程名:"+Thread.currentThread().getName()+" 进入");//4、副线程进入睡眠,转去执行主线程try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("当前线程名: "+Thread.currentThread().getName()+ "企图调用B实例的last方法");//9、副线程想要执行a.last()方法就需要锁a,但是此时锁a在主线程手中,副线程无法执行,转去执行主线程a.last();}public synchronized void last(){System.out.println("进入了A类的last方法内部");}
}

8、ReentrantLock implements Lock

第三种解决线程问题的方法:

* 1、创建ReentrantLook类的对象

* 2、将需要进行同步的代码放在try语句中

* 3、在语句的第一行调用ReentrantLock的lock()方法将代码锁定,使之变为单线程

* 4、在finally语句中将代码的锁打开,使CPU可以去执行其他的线程

* 注意:

        如果不打开线程锁的话,那么就只会执行一个线程直至线程执行完毕,其他的线程无法获得cpu的执行权

        Lock的实现类的对象也得是唯一的,锁是一个锁。如果是Thread子类创建的线程就需要将

ReentarntLock的对象设置为静态对象

package lock;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;*  本程序主要介绍的是利用实现了接口Lock的类ReentrantLock来解决线程的安全问题
*  方法如下:
*       1、创建ReentrantLook类的对象
*       2、将需要进行同步的代码放在try语句中
*       3、在语句的第一行调用ReentrantLock的lock()方法将代码锁定,使之变为单线程
*       4、在finally语句中将代码的锁打开,使CPU可以去执行其他的线程
*   注意:如果不打开线程锁的话,那么就只会执行一个线程直至线程执行完毕,其他的线程无法获得cpu的执行权public class Really_lock {public static void main(String[] args) {Window4 w=new Window4();Thread t1=new Thread(w);Thread t2=new Thread(w);Thread t3=new Thread(w);t3.setName("线程3");t2.setName("线程2");t1.setName("线程1");t1.start();t2.start();t3.start();}
}
class Window4 implements Runnable {private int tickets=100;//1、创建ReentrantLook类的对象private ReentrantLock lock=new ReentrantLock();@Overridepublic void run() {while(true) {//2、将需要进行同步的代码放在try语句中3、在语句的第一行调用ReentrantLock的lock()方法将代码锁定,使之变为单线程try {//3、在语句的第一行调用ReentrantLock的lock()方法将代码锁定,使之变为单线程lock.lock();{if (tickets > 0) {System.out.println(Thread.currentThread().getName() + "卖票,票号为" + tickets);tickets--;} else {break;}}} finally{lock.unlock();}}}
}

Lock和synchronized的异同:

a.Lock是显式锁(需要手动开启和关闭,千万不要忘记关闭锁),synchronized是隐式锁,出了作用域就会自动释放

b.Lock只有代码块锁,sychronized有代码块锁以及方法锁

c.使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

优先使用顺序:

Lock-->同步代码块-->(已经进入方法体,分配了相应资源)-->同步方法(在方法体之外)

会释放锁的操作

当前线程的同步方法、同步代码块执行结束

当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行

当前线程在同步代码块、同步方法中出现了未处理的Error或Exception、导致异常结束

当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁

9、线程的通讯

*  线程的通讯
*  wait():一旦执行此方法,当前线程就会进入阻塞状态,并释放同步监视器
*  notify():一旦执行此方法,就会唤一个被wait的线程。如果有多个线程被wait,那么优先级越高的越有可能被唤醒
*  notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
*  说明:wait(),notify(),notifyAll()三个方法必须使用在同步代码块或者同步方法之中
*             wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则就会出现IllegalMonitorStateException异常
*   这样的话,既然任何类都可以作为同步监视器,那么也就是说每一个类的对象都可以调用者三个方法,这么说来这三个方法应当存在于每一个类之中,因此这三个方法应该是被定义在Object类之中的,事实上确实如此

*需要注意的是,调用wait(),notify(),notifyAll()三个方法的对象就是同步监视器,也就是如果不是默认用this或使用类.class的同步代码块,在调用这三个方法时就需要写明方法的调用者(在以下的代码之中已经用注释写出)

*  关于wait()和sleep()方法的异同:

*       相同点:一旦执行此方法,都可以使线程进入阻塞状态
*       不同点:a.两个方法的声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
*                     b.调用的要求不同:sleep()在任何需要的情境下都可以使用。wait()必须使用在同步代码块或者同步方法之中
*                    c.关于是否释放同步监视器:如果两个方法都使用在同步代码块或者同步方法之中,sleep()不会释放同步监视器,而wait()会释放同步监视器
*                 关于c,我想起来之前写了一个关于死锁的代码,也就是嵌套的synchronized,在第一个线程在拿到锁A之后sleep(),此时的锁A仍然在他的手中,在第二个线程拿到锁B之后*sleep(),但是此时的锁B还在第二个线程手中,然后CPU去执行第一个线程,但是第一个线程无法获取在第二个线程的锁B,第二个线程也无法获取在第一个线程手中的锁A,因此两个*线程陷入死锁。由此可以证明sleep()方法确实不会释放同步监视器。

来吧,展示:

本程序旨在实现两个线程交替的打印0~100的数字。

package About_Thread;
*  线程的通讯
*  本程序旨在实现两个线程交替的打印0~100的数字。
*  wait():一旦执行此方法,当前线程就会进入阻塞状态,并释放同步监视器
*  notify():一旦执行此方法,就会唤一个被wait的线程。如果有多个线程被wait,就会唤醒优先级较高的线程
*  notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
*  说明:wait(),notify(),notifyAll()三个方法必须使用在同步代码块或者同步方法之中
*       wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则就会出现IllegalMonitorStateException异常
*   这样的话,既然任何类都可以作为同步监视器,那么也就是说每一个类的对象都可以调用者三个方法,这么说来这三个方法应当存在于每一个类之中,因此这三个方法应该
*   是被定义在Object类之中的,事实上也是如此
*
*  关于wait()和sleep()方法的异同:
*       相同点:一旦执行此方法,都可以使线程进入阻塞状态
*       不同点:a.两个方法的声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
*             b.调用的要求不同:sleep()在任何需要的情境下都可以使用。wait()必须使用在同步代码块或者同步方法之中
*             c.关于是否释放同步监视器:如果两个方法都使用在同步代码块或者同步方法之中,sleep()不会释放同步监视器,而wait()会释放同步监视器
*        关于c,我想起来之前写了一个关于死锁的代码,也就是嵌套的synchronized,在第一个线程在拿到锁A之后sleep(),此时的锁A仍然在他的手中,在第二个线程
*   拿到锁B之后sleep(),但是此时的锁B还在第二个线程手中,然后CPU去执行第一个线程,但是第一个线程无法获取在第二个线程的锁B,第二个线程也无法获取在第一个
*   线程手中的锁A,因此两个线程陷入死锁。由此可以证明sleep()方法确实不会释放同步监视器。public class Communication {public static void main(String[] args) {Print p=new Print();Thread t1=new Thread(p);Thread t2=new Thread(p);Thread t3=new Thread(p);t1.setName("线程1");t2.setName("线程2");//t3.setName("线程3");t1.setPriority(10);t2.setPriority(1);//t3.setPriority(10);t1.start();t2.start();//t3.start();}}
class Print implements Runnable {private int num=1;// private Object obj=new Object();@Overridepublic void run() {while(true){//synchronized (obj)synchronized (this){//1、唤醒一个线程//obj.notify();notify();if(num<101){System.out.println(Thread.currentThread().getName()+":"+num);num++;try {//2、使当前线程陷入阻塞状态// obj.wait();wait();} catch (InterruptedException e) {e.printStackTrace();}}else{break;}}}}
}

10、线程创建的新方式

 创建线程的第三种方式:实现Callable接口
*       1、构造Callable的实现类
*       2、实现call()方法,将需要在线程中执行的代码放在call()方法之中
*       3、构造Callable实现类的子类的对象
*       4、以Callable实现类的子类的对象为参数构造FutureTask的对象
*       5、以FutureTask的对象为参数构造Thread的对象
*       6、通过Thread的对象调用start(),新线程中运行的就是call()。(start()方法调用了call())
*       7、通过FutureTask的get()方法可以得到Callable实现类的call()方法的返回值
*  实现Callable接口比实现Runnable接口要强大:
*       1、相比run()方法,call()方法有返回值
*       2、方法可以抛出异常
*       3、可以支持泛型的返回值
*       4、需要借助FutureTask类,比如获取返回结果

package About_Thread;*  创建线程的第三种方式:实现Callable接口
*       1、构造Callable的实现类
*       2、实现call()方法,将需要在线程中执行的代码放在call()方法之中
*       3、构造Callable实现类的子类的对象
*       4、以Callable实现类的子类的对象为参数构造FutureTask的对象
*       5、以FutureTask的对象为参数构造Thread的对象
*       6、通过Thread的对象调用start(),新线程中运行的就是call()。(start()方法调用了call())
*       7、通过FutureTask的get()方法可以得到Callable实现类的call()方法的返回值
*  实现Callable接口比实现Runnable接口要强大:
*       1、相比run()方法,call()方法有返回值
*       2、方法可以抛出异常
*       3、可以支持泛型的返回值
*       4、需要借助FutureTask类,比如获取返回结果import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//1、构造Callable的实现类
class NewThread implements Callable{//2、实现call()方法,将需要在线程中执行的代码放在call()方法之中@Overridepublic Object call() throws Exception {int sum=0;for (int i = 0; i < 10; i++) {if(i%2==1){sum+=i;}System.out.println(i);}//自动装包return sum;}
}
public class Third_Thread {public static void main(String[] args) {//3、构造Callable实现类的子类的对象NewThread newThread = new NewThread();//4、以Callable实现类的子类的对象为参数构造FutureTask的对象FutureTask futureTask = new FutureTask(newThread);//Ctrl + Alt + U 可以查看类的继承图//FutureTask类实现了RunnableFuture接口,而RunnableFuture继承了Runnable, Future接口,也就是说FutureTask类实现了Runnable接口//5、以FutureTask的对象为参数构造Thread的对象//6、通过Thread的对象调用start(),新线程中运行的就是call()。(start()方法调用了call())new Thread(futureTask).start();try {//如果没有启动线程,那么get()方法就无法获取返回值,程序无输出,不报错也不停止//FutureTask对象的get()方法获取的是Callable的实现类重写的call()方法的返回值、//自动拆包//7、通过FutureTask的get()方法可以得到Callable实现类的call()方法的返回值System.out.println("奇数和为:"+futureTask.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
}

创建线程的第四种方式: 创建线程池

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

思路:提前创建好多个线程,放入线程池中,使用是直接获取,使用玩放入池中。这样就可以避免频繁的创建销毁线程,实现线程的重复利用。类似生活中的公交车

好处:

a.提高相应速度(减少了创建新线程的时间)

b.降低资源损耗(重复利用线程池中的线程,不需要每次都创建新线程)

c.便于线程管理

corePooleSize:线程池的大小

maxmumPoolSize:最大线程数

keepAliveTime:线程没有任务是最多保存多长时间后悔终止

●JDK 5.0起提供了线程池相关API: ExecutorService和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
        ➢void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行
Runnable
        ➢<T> Future<T> submit(Callable<T> task): 执行任务,有返回值,一般又来执行
Callable
        ➢void shutdown():关闭连接池
●Executors:工具类、线程池的工厂^类,用于创建并返回不同类型的线程池
        ➢Executors .newCachedThreadPool():创建一个 可根据需要创建新线程的线程池
        ➢Executors.newFixedThreadPool(n); 创建一个可 重用固定线程数的线程池
        ➢Executors.newSingleThreadExecutor(): 创建一个 只有一个线程的线程池
        ➢Executors.newScheduledThreadPool(n): 创建一个线程池,它可安排在给定延迟后运
行命令或者定期地执行。

package About_Thread;import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;class Thread_text implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2==1)System.out.println(Thread.currentThread().getName()+":"+i);}}
}
class Subclass extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2==0)System.out.println(Thread.currentThread().getName()+":"+i);}}
}
public class Thread_pool {public static void main(String[] args) {//创建一个线程池//public interface ExecutorService extends ExecutorExecutorService executorService = Executors.newFixedThreadPool(3);//只能开启实现了Runnable接口的类的线程,不能开启实现了Callable接口的类的线程executorService.execute(new Thread_text());//开启通过Callable接口,也可以开启实现了Runnable接口的类的线程executorService.submit(new Thread_text());//NewThread类来自于同一包下的Third_Thread文件之中,作用是打印0~9executorService.submit(new NewThread());}
}

11、Idea快捷键

九、常用类

1、String

(1)、基础知识

*   常用类之String:
*       1、字符串,使用一对""表示
*       2、String类的声明是final的,不可以被继承
*       3、String类实现了Serializable接口:表示字符串是可以支持序列化的
*            String类实现了Comparable接口:表示字符串可以比较大小
*       4、String类内部定义了final char[] value用来储存字符串数据,final可以保证value的引用地址不会被修改
*       5、String类:代表了不可变的字符序列。简称,不可变性
*           体现:   a.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
*                         b.当对现有的字符串进行拼接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
*                         c.当调用String的replace()方法时修改指定字符或字符串是,也需要重新指定内存区域赋值
*       6、通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池之中, 变量储存的是常量池中字符串的的地址
*       7、通过new方法创建的字符串数据存储在堆区,变量中存储的是指向堆区的地址。但是处于堆区的字符串对象中的char[]指向的是处于字符串常量池中的等值的字符串,也就是说char[]中储存的是指向字符串常量池的地址。因此,在用new方法创建String对象的时候,可能会创建两个对象,一个是new出来的存放在堆区的对象,另外一个是char[]
*       8、字符串常量池中不会存储内容相同的字符串
*       9、在字面量方法创建String对象时,会现在字符串常量池中寻找新创建的字符串的值是否已经存在,如果已经存在就不会创建新的字符串,而是直接使用已有的地址,如果不存在的话就会创建新的字符串
*       10、当改变一个字符串的时候,不会在原有的字符串上进行改变,而是会在常量池创建一个新的字符串(创造之前也需要搜索一下该字符是否在常量池中存在)


*   常用类之String:
*       1、字符串,使用一对""表示
*       2、String类的声明是final的,不可以被继承
*       3、String类实现了Serializable接口:表示字符串是可以支持序列化的
*          String类实现了Comparable接口:表示字符串可以比较大小
*       4、String类内部定义了final char[] value用来储存字符串数据,final可以保证value的引用地址不会被修改
*       5、String类:代表了不可变的字符序列。简称,不可变性
*           体现: a.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
*                  b.当对现有的字符串进行拼接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
*                  c.当调用String的replace()方法时修改指定字符或字符串是,也需要重新指定内存区域赋值
*       6、通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池之中, 变量储存的是常量池中字符串的的地址
*       7、通过new方法创建的字符串数据存储在堆区,变量中存储的是指向堆区的地址。但是处于堆区的字符串对象中的char[]指向的是处于字符串常量池中的等值的
*       字符串,也就是说char[]中储存的是指向字符串常量池的地址。因此,在用new方法创建String对象的时候,可能会创建两个对象,一个是new出来的存放在堆区的
*       对象,另外一个是char[]
*       8、字符串常量池中不会存储内容相同的字符串
*       9、在字面量方法创建String对象时,会现在字符串常量池中寻找新创建的字符串的值是否已经存在,如果已经存在就不会创建新的字符串,而是
*       直接使用已有的地址,如果不存在的话就会创建新的字符串
*       10、当改变一个字符串的时候,不会在原有的字符串上进行改变,而是会在常量池创建一个新的字符串(创造之前也需要搜索一下该字符是否在常量池中存在)
*public class String_text1 {@Testpublic void method1(){String str1="abc";  使用字面量为字符串赋值,此时的字符串值声明在字符串常量池之中String str2="abc";  在字面量方法创建String对象时,会现在字符串常量池中寻找新创建的字符串的值是否已经存在,如果已经存在就不会创建新的字符串而是直接使用已有的地址,如果不存在的话就会创建新的字符串System.out.println(str1 == str2);   ture  str1和str2指向的是常量池的同一个字符串,地址相同String str3=new String("abc");System.out.println(str3 == str1);    false 通过new方法创造的字符串存储在堆区当改变一个字符串的时候,不会在原有的字符串上进行改变,而是会在常量池创建一个新的字符串str3="abc";System.out.println(str3 == str1);   //ture我们尝试改变str1的值,看看会不会改变str2的值str1="abcm";System.out.println(str2);   abc  str2的值没有改变,所以虚拟机在常量池创建了一个新的字符串,而str1指向该字符串String str4="abc";str4.replace('a','m');System.out.println(str4);   abc 字符串不可以改变replace方法用一个字符去代替原来的字符String str5=str4.replace('a','d');System.out.println(str5);String str6="aaa";System.out.println(str6.replace('a','c'));  //cccSystem.out.println("****************************");String str7=new String("aaa");System.out.println(str7);str7="bbbb";System.out.println(str7);}
}

对象中的字符串对象:通过字面量的方式赋值,两个字符串对象指向指向方法区的字符串常量池的同一位置

@Testpublic void method2(){//方法中的字符赋值Person junbao = new Person("Junbao", 20);Person junbao1 = new Person("Junbao", 20);System.out.println(junbao.name.equals(junbao1.name));   //true   String类重写了equals()方法System.out.println(junbao.name == junbao1.name);    //true   通过字面量的方式赋值,两个name指向指向方法区的字符串常量池的同一位置junbao.name="jack";System.out.println(junbao.name == junbao1.name);    //false}

对字符串的赋值:
     只要是最后一次赋值(包括创建)是通过字面量赋值的存储的是指向字符串常量池的地址
     只要是最后一次赋值(包括创建)设计到了另外一个变量的存储的是指向字符串常量池的地址
     常量(包括final修饰的)与常量的拼接结果在常量池中,且常量池中不会存在相同内容的常量
     只要其中一个是变量,那么结果就在堆上
     如拼接的结果调用intern()方法,返回值就在常量池中

对字符串的赋值:只要是最后一次赋值(包括创建)是通过字面量赋值的存储的是指向字符串常量池的地址只要是最后一次赋值(包括创建)设计到了另外一个变量的存储的是指向字符串常量池的地址常量(包括final修饰的)与常量的拼接结果在常量池中,且常量池中不会存在相同内容的常量只要其中一个是变量,那么结果就在堆上如拼接的结果调用intern()方法,返回值就在常量池中@Testpublic void method3(){String s1="a";String s2="b";String s3=new String("a");String s4=new String("b");System.out.println(s1 == s3);//falses3="a";System.out.println(s1 == s3);//trueString s5="a"+"b";String s6=s1+"b";String s7="a"+s2;String s8="a"+"b";String s9="ab";String s10=new String(s1+s2);System.out.println(s5==s7);//false 因为s5由有字面量赋值的,s5存储的是指向字符串常量池的地址System.out.println(s6==s7);//false 因为s7涉及到了另外一个字符串变量,所以其实相当通过于new创造的对象System.out.println(s8 == s9);//true 因为s8和s9通过字面量进行赋值,所以他们存储的是指向字符串常量池中的地址System.out.println(s10 == s9);//falseString s11=s7.intern();System.out.println(s11 == s5);//true intern()方法可以将任何强制返回一个指向字符串常量池的地址//final修饰的是常量final String str12="a";String str13=str12+"b";System.out.println(str13==s9);//true}

(2)、常用方法(无脑版)

*       本程序旨在测试一些String常用的方法:
*           int length(); 获取字符串的长度
*           char charAt(int index);   返回某索引出的字符 return value[index]
*           boolean isEmploy();    判断是否是空字符串 return value.length==0
*           String toLowerCase();将字符串的的所有字符转变为小写并返回,但是原字符串不变
*           String toUpperCase(): 将字符串的的所有字符转变为大写并返回,但是原字符串不变
*           String trim();  将字符串首尾的空白去除,并返回字符串,原字符串不变
*           bool equals(Object obj): 比较两个字符串的内容是否相同,已重写,不测试
*           bool equalsIgnoreCase(String anotherString); 忽略大小写,判断两个字符串是否相同
*           String concat(String str): 将指定的字符串连接到此字符串结尾。等价与用“+”
*           int compareTo(String anotherString);  比较两个字符串的大小,并返回两个字符串第一个不同的字符之间的差(char-->int)
*           String substring(int beginIndex): 返回一个新的字符串,他是此字符串的从beginIndex开始向后截取知道最后一个字符(包括最后一个字符)
*           String substring(int beginIndex,int endIndex): 返回一个新的字符串,他是此字符串的从beginIndex开始向后截取知道endIndex(不包括最后一个字符),也就是左闭右开
*           boolean endsWith(String suffix); 测试此字符串中是否以指定的后缀结束
*           boolean startsWith(String prefix): 测试此字符串是否以指定的前缀开始
*           boolean startsWith(String prefix,int toffset): 测试此字符串从指定索引是否以指定的前缀开始
*           boolean contains(CharSequence s): 当且仅当此字符串中包含指定的char值序列(序列就是字符串)是返回true
*           int indexOf(String str): 返回指定字符串在此字符串中第一次出现的位置的索引,没有出现返回-1
*           int indexOf(String str,int fromIndex): 从指定的位置开始向右,返回指定字符串在此字符串中第一次出现的位置的索引,没有出现返回-1,
*           int lastIndexOf(String str):返回指定字符串在此字符串中最右边出现的索引
*           int lastIndexOf(String str,int formIndex):从指定的位置向左,返回指定字符串在此字符串中最右边出现的索引
*           String replace(char oldChar,char newChar); 返回一个新的字符串,使newChar替换此字符串中所有的oldChar
*           String replace(char oldChar,char newChar); 返回一个新的字符串,使newChar替换此字符串中所有的oldChar
*           String replace(CharSequence target,CharSequence replacement) 使用指定的字面值替换此字符串中的所有的匹配字面值目标序列的字符串
*           char[] toCharArray():将字符串中的全部字符放在一个字符数组中

代码很长,不建议看:

package StringKnowledge;*       本程序旨在测试一些String常用的方法:
*           int length(); 获取字符串的长度
*           char charAt(int index);   返回某索引出的字符 return value[index]
*           boolean isEmploy();    判断是否是空字符串 return value.length==0
*           String toLowerCase();将字符串的的所有字符转变为小写并返回,但是原字符串不变
*           String toUpperCase(): 将字符串的的所有字符转变为大写并返回,但是原字符串不变
*           String trim();  将字符串首尾的空白去除,并返回字符串,原字符串不变
*           bool equals(Object obj): 比较两个字符串的内容是否相同,已重写,不测试
*           bool equalsIgnoreCase(String anotherString); 忽略大小写,判断两个字符串是否相同
*           String concat(String str): 将指定的字符串连接到此字符串结尾。等价与用“+”
*           int compareTo(String anotherString);  比较两个字符串的大小,并返回两个字符串第一个不同的字符之间的差(char-->int)
*           String substring(int beginIndex): 返回一个新的字符串,他是此字符串的从beginIndex开始向后截取知道最后一个字符(包括最后一个字符)
*           String substring(int beginIndex,int endIndex): 返回一个新的字符串,他是此字符串的从beginIndex开始向后截取知道endIndex
*            (不包括最后一个字符),也就是左闭右开
*           boolean endsWith(String suffix); 测试此字符串中是否以指定的后缀结束
*           boolean startsWith(String prefix): 测试此字符串是否以指定的前缀开始
*           boolean startsWith(String prefix,int toffset): 测试此字符串从指定索引是否以指定的前缀开始
*           boolean contains(CharSequence s): 当且仅当此字符串中包含指定的char值序列是返回true
*           int indexOf(String str): 返回指定字符串在此字符串中第一次出现的位置的索引,没有出现返回-1
*           int indexOf(String str,int fromIndex): 从指定的位置开始向右,返回指定字符串在此字符串中第一次出现的位置的索引,没有出现返回-1,
*           int lastIndexOf(String str):返回指定字符串在此字符串中最右边出现的索引
*           int lastIndexOf(String str,int formIndex):从指定的位置向左,返回指定字符串在此字符串中最右边出现的索引
*           String replace(char oldChar,char newChar); 返回一个新的字符串,使newChar替换此字符串中所有的oldChar
*           String replace(char oldChar,char newChar); 返回一个新的字符串,使newChar替换此字符串中所有的oldChar
*           String replace(CharSequence target,CharSequence replacement) 使用指定的字面值替换此字符串中的所有的匹配字面值目标序列的字符串
*           char[] toCharArray():将字符串中的全部字符放在一个字符数组中import java.util.Arrays;
import java.util.Locale;public class StringMethodText {public static void main(String[] args) {String s1="abc";//int length(); 获取字符串的长度System.out.println(s1.length());//3//char charAt(int index);   返回某索引出的字符 return value[index]System.out.println(s1.charAt(0));//a//boolean isEmploy();    判断是否是空字符串 return value.length==0String s2=" ";//空格String s3="";//空System.out.println(s1.isEmpty());//falseSystem.out.println(s2.isEmpty());//falseSystem.out.println(s3.isEmpty());//true//String toLowerCase();将字符串的的所有字符转变为小写并返回,但是原字符串不变//String toUpperCase(): 将字符串的的所有字符转变为大写并返回,但是原字符串不变String s4="ABC";String s5=s4.toLowerCase();System.out.println(s5);//abcSystem.out.println(s4);//ABC//String trim();  将字符串首尾的空白去除,并返回字符串,原字符串不变s4="  ccc  ";s5=s4.trim();System.out.println("-----"+s4+"---");//----  ccc  --System.out.println("-----"+s5+"---");//-----ccc---//bool equals(Object obj): 比较两个字符串的内容是否相同,已重写,不测试//bool equalsIgnoreCase(String anotherString); 忽略大小写,判断两个字符串是否相同//String concat(String str): 将指定的字符串连接到此字符串结尾。等价与用“+”//int compareTo(String anotherString);  比较两个字符串的大小,并返回两个字符串第一个不同的字符之间的差(char-->int)s4="abc";s5="acg";System.out.println(s4.compareTo(s5));//-1//String substring(int beginIndex): 返回一个新的字符串,他是此字符串的从beginIndex开始向后截取知道最后一个字符(包括最后一个字符)String s6="0123456789";System.out.println(s6.substring(5));//56789//String substring(int beginIndex,int endIndex): 返回一个新的字符串,他是此字符串的从beginIndex开始向后截取知道endIndex// (不包括最后一个字符),也就是左闭右开System.out.println(s6.substring(2,5));//234//boolean endsWith(String suffix); 测试此字符串中是否以指定的后缀结束s4="123456";System.out.println(s4.endsWith("56"));//trueSystem.out.println(s4.endsWith("12"));//false//boolean startsWith(String prefix): 测试此字符串是否以指定的前缀开始System.out.println(s4.startsWith("123"));//trueSystem.out.println(s4.startsWith("34"));//false// //boolean startsWith(String prefix,int toffset): 测试此字符串从指定索引是否以指定的前缀开始System.out.println(s4.startsWith("45",3));//true//boolean contains(CharSequence s): 当且仅当此字符串中包含指定的char值序列是返回trueSystem.out.println(s4.contains("23"));//trueSystem.out.println(s4.contains("567"));//false//int indexOf(String str): 返回指定字符串在此字符串中第一次出现的位置的索引,没有出现返回-1System.out.println(s4.indexOf("45"));//3System.out.println(s4.indexOf("478"));//-1//int indexOf(String str,int fromIndex): 从指定的位置开始向右,返回指定字符串在此字符串中第一次出现的位置的索引,没有出现返回-1,s4="123412";System.out.println(s4.indexOf("12",2));//4//int lastIndexOf(String str):返回指定字符串在此字符串中最右边出现的索引s4="123123123";System.out.println(s4.lastIndexOf("123"));//6//int lastIndexOf(String str,int formIndex):从指定的位置向左,返回指定字符串在此字符串中最右边出现的索引System.out.println(s4.lastIndexOf("123",6));//6 卧槽 为啥是6??? 是不是把位置以后的也算上了System.out.println(s4.lastIndexOf("123",5));//3//String replace(char oldChar,char newChar); 返回一个新的字符串,使newChar替换此字符串中所有的oldChars5=s4.replace('1','2');System.out.println(s5);//223223223//String replace(CharSequence target,CharSequence replacement) 使用指定的字面值替换此字符串中的所有的匹配字面值目标序列的字符串s5=s4.replace("123","456");System.out.println(s5);//char[] toCharArray():将字符串中的全部字符放在一个字符数组中char[] ch=s4.toCharArray();System.out.println(Arrays.toString(ch));//[1, 2, 3, 1, 2, 3, 1, 2, 3]}
}

(3)、String与包装类以及基本数据类型之间的转换

*  本程序旨在测试String类与基本数据类型和包装类之间的转化
*      将其他类型转化为String类,可以使用静态方法ValueOf(数据)  该数据可以是Object及其子类的对象,也可以是基本数据类型(转化的字符串在堆区)

*        String s=String.valueOf(num);

*      int num1=Integer.parseInt(String str);
*      Integer i2=Integer.parseInt(String str);(建议用)
*      Integer i1=new Integer(Integer.parseInt(String str));(不建议用)

package StringKnowledge;
/*
*  本程序旨在测试String类与基本数据类型和包装类之间的转化
*      将其他类型转化为String类,可以使用静态方法ValueOf(数据)  该数据可以是Object及其子类的对象,也可以是基本数据类型(转化的字符串在堆区)
*      int num1=Integer.parseInt(String str);
*      Integer i2=Integer.parseInt(String str);
*      Integer i1=new Integer(Integer.parseInt(String str));
*/
public class TypeTranslate {public static void main(String[] args) {int num=10;String s=String.valueOf(num);//转化的字符串在堆区System.out.println(s);Integer i=num;s=String.valueOf(i);System.out.println(s);String s1="10";System.out.println(s == s1);//false//将String转化为基本数据类型int num1=Integer.parseInt(s);System.out.println(num1);//将String转化为包装类Integer i1=new Integer(Integer.parseInt(s));System.out.println(i1);Integer i2=Integer.parseInt(s);System.out.println(i2);}
}

(4)、解码与编码

*       本程序旨在测试字符串与byte数组之间的转化:
*       String(byte[]) :通过使用平台的默认字符集来解码指定的byte数组,构成了一个新的字符串
*       String(byte[],int start,int length):用指定的字节数组的一部分,即从数组的起始位置start开始选取length个字节构造一个字符串对象
*
*       public byte[] getBytes():使用平台默认字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中
*       public byte[] getBytes(String charsetName):使用指定的字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中
*
*       编码:字符串--->字节(看得懂--->看不懂的二进制数据)
*       解码:解码的逆过程,字节--->字符串(看不懂的二进制数据--->看的懂)
*       所以出现乱码的原因就是解码和编码的字符集不一致

package StringKnowledge;*       本程序旨在测试字符串与byte数组之间的转化:
*       String(byte[]) :通过使用平台的默认字符集来解码指定的byte数组,构成了一个新的字符串
*       String(byte[],int start,int length):用指定的字节数组的一部分,即从数组的起始位置start开始选取length个字节构造一个字符串对象
*
*       public byte[] getBytes():使用平台默认字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中
*       public byte[] getBytes(String charsetName):使用指定的字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中
*
*       编码:字符串--->字节(看得懂--->看不懂的二进制数据)
*       解码:解码的逆过程,字节--->字符串(看不懂的二进制数据--->看的懂)
*       所以出现乱码的原因就是解码和编码的字符集不一致import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;public class StringAndByte {public static void main(String[] args) throws UnsupportedEncodingException {byte[] b=new byte[]{65,66,67,68,69,70};//String(byte[]) :通过使用平台的默认字符集来解码指定的byte数组,构成了一个新的字符串String str=new String(b);System.out.println(str);//ABCDEF//String(byte[],int start,int length):用指定的字节数组的一部分,即从数组的起始位置start开始选取length个字节构造一个字符串对象String str1=new String(b,2,4);System.out.println(str1);//CDEFString str2="abcdefg";//public byte[] getBytes():使用平台默认字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中byte b1[]=str2.getBytes(StandardCharsets.UTF_8);//参数是字符集,表示该操作是用UTF_8来进行编码的(默认的就是UTF_8)String str3="abcdefg中国";System.out.println(Arrays.toString(b1));//[97, 98, 99, 100, 101, 102, 103]//public byte[] getBytes(String charsetName):使用指定的字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中byte b2[]=str3.getBytes("gbk");//用GBK的字符集来进行编码System.out.println(Arrays.toString(b2));//[97, 98, 99, 100, 101, 102, 103, -42, -48, -71, -6]//举一个出现乱码的例子:String str4= new String(b2);System.out.println(str4);//abcdefg�й�  原因是str3用GBK的字符集来进行编码,但是却用了UTF-8来进行解码,编码解码的字符集不一致}
}

(5)、StringBuffer和StringBuilder

*   本程序主要介绍的是String、StringBuffer、StringBuilder
*   String、StringBuffer、StringBuilder三者的异同:
*       String为不可变的字符序列,底层使用char[]存储
*       StringBuffer:可以改变的字符序列,线程是安全的,但是效率低,底层使用char[]存储
*       StringBuilder:可变的字符序列,线程不安全,效率高,底层使用char[]存储
*       StringBuffer sb2=new StringBuffer("abc");//char[] value=new char["abc".length()+16] 创造了一个字符长度加上16的数组
*       StringBuffer sb1=new StringBuffer();//char[] value=new char[16]; 默认创造了一个容量为16的数组
*
*       扩容问题:如果要添加的数据底层数组无法承载,那么就需要扩充底层的数组容量,
*       默认情况下数组容量扩充为原来的2倍+2,同时将原来数组的元素赋值到新的数组上
*
*       开发中建议使用:StringBuffer(int capacity)或者StringBuilder(int capacity),因为扩容需要复制会影响影响效率

package StringKnowledge;*   本程序主要介绍的是String、StringBuffer、StringBuilder
*   String、StringBuffer、StringBuilder三者的异同:
*       String为不可变的字符序列,底层使用char[]存储
*       StringBuffer:可以改变的字符序列,线程是安全的,但是效率低,底层使用char[]存储
*       StringBuilder:可变的字符序列,线程不安全,效率高,底层使用char[]存储
*       StringBuffer sb2=new StringBuffer("abc");//char[] value=new char["abc".length()+16] 创造了一个字符长度加上16的数组
*       StringBuffer sb1=new StringBuffer();//char[] value=new char[16]; 默认创造了一个容量为16的数组
*
*       扩容问题:如果要添加的数据底层数组无法承载,那么就需要扩充底层的数组容量,
*       默认情况下数组容量扩充为原来的2倍+2,同时将原来数组的元素赋值到新的数组上
*
*       开发中建议使用:StringBuffer(int capacity)或者StringBuilder(int capacity),因为扩容需要复制会影响影响效率public class StringBufferText {public static void main(String[] args) {String str=new String();//char[] value=new char[0]; 创造了一个容量为0的数组String str1=new String("abc");//char[] value=new char[]{'a','b','c'}; 创造了一个容量为3的数组StringBuffer sb1=new StringBuffer();//char[] value=new char[16]; 默认创造了一个容量为16的数组StringBuffer sb2=new StringBuffer("abc");//char[] value=new char["abc".length()+16] 创造了一个字符长度加上16的数组//append(char ch)方法在字符串结尾加上一个字符sb2.append('d');//value[3]='d';System.out.println(sb2);//abcdSystem.out.println(sb2.length());//4}
}

StringBuffer类的常用方法:

*   本程序主要测试StringBuffer类的一些方法
*           StringBuffer append(xxx) ;提供多种append()方法,进行字符串的拼接,可以是数字,字符,字符,boolean
*           StringBuffer delete(int star,int end):删除指定位置的内容,左闭右开
*           StringBuffer replace(int start,int end,String str): 将[start,end)位置替换为str,会改变原字符串
*           StringBuffer insert(int set,xxx):在指定位置插入xxx,会改变原字符串
*           StringBuffer reverse(String str):翻转字符,会改变原字符串
*           public String substring(int start,int end):返回一个从start开始到end结束的左闭右开的子字符串
*           public int indexOf(String str):返回指定字符串在此字符串中第一次出现的位置的索引,没有出现返回-1
*           public int length()
*           public char charAt()
*           public void setCharAt(int n,char ch):将第n个字符换为ch

package StringKnowledge;*   本程序主要测试StringBuffer类的一些方法
*           StringBuffer append(xxx) ;提供多种append()方法,进行字符串的拼接,可以是数字,字符,字符,boolean
*           StringBuffer delete(int star,int end):删除指定位置的内容,左闭右开
*           StringBuffer replace(int start,int end,String str): 将[start,end)位置替换为str,会改变原字符串
*           StringBuffer insert(int set,xxx):在指定位置插入xxx,会改变原字符串
*           StringBuffer reverse(String str):翻转字符,会改变原字符串
*           public String substring(int start,int end):返回一个从start开始到end结束的左闭右开的子字符串
*           public int indexOf(String str):返回指定字符串在此字符串中第一次出现的位置的索引,没有出现返回-1
*           public int length()
*           public char charAt()
*           public void setCharAt(int n,char ch):将第n个字符换为chpublic class StringBufferMethod {public static void main(String[] args) {StringBuffer s1=new StringBuffer("abc");//StringBuffer append(xxx) ;提供多种append()方法,进行字符串的拼接,可以是数字,字符,字符,booleans1.append(1);s1.append("1");System.out.println(s1);//abc11//StringBuffer delete(int star,int end):删除指定位置的内容,左闭右开s1.delete(1,2);System.out.println(s1);//ac11//StringBuffer replace(int start,int end,String str): 将[start,end)位置替换为str,会改变原字符串StringBuffer ddd = s1.replace(1, 3,"112");//a1121  左闭右开System.out.println(ddd);//a1121System.out.println(s1);//a1121//StringBuffer insert(int set,xxx):在指定位置插入xxx,会改变原字符串StringBuffer s2=new StringBuffer(s1.insert(2,2222));System.out.println(s1);//a12222121System.out.println(s2);//a12222121//StringBuffer reverse(String str):翻转字符,会改变原字符串StringBuffer reverse = s1.reverse();System.out.println(s1);//12122221aSystem.out.println(reverse);//12122221a//public String substring(int start,int end):返回一个从start开始到end结束的左闭右开的子字符串//public int indexOf(String str):返回指定字符串在此字符串中第一次出现的位置的索引,没有出现返回-1//public int length()//public char charAt()//public void setCharAt(int n,char ch):将第n个字符换为chs1.setCharAt(0,'5');System.out.println(s1);}
}

(6)三种字符串的效率的比较

效率的从高到低的排列:StringBuilder>StringBuffer>String

package StringKnowledge;本程序旨在测试String、StringBuffer、StringBuilder对相同操作的实行时间结果表明:效率的从高到低的排列:StringBuilder>StringBuffer>StringSystem.currentTimeMillis()产生一个当前的毫秒,这个毫秒其实就是自1970年1月1日0时起的毫秒数System.out.println(System.currentTimeMillis());public class CompareThree {public static void main(String[] args) {long startTime=0L;long endTime=0L;String s="";StringBuffer buffer=new StringBuffer("");StringBuilder builder=new StringBuilder("");startTime=System.currentTimeMillis();for (int i = 0; i < 200000; i++) {buffer.append(String.valueOf(i));}endTime=System.currentTimeMillis();System.out.println("StringBUffer的执行时间为:"+(endTime-startTime));startTime=System.currentTimeMillis();for (int i = 0; i < 200000; i++) {builder.append(String.valueOf(i));}endTime=System.currentTimeMillis();System.out.println("StringBUilder的执行时间是:"+(endTime-startTime));startTime=System.currentTimeMillis();for (int i = 0; i < 200000; i++) {s+=i;}endTime=System.currentTimeMillis();System.out.println("string的执行时间是:"+(endTime-startTime));}
}

2、Date

*        java.util.Date
*               |---java.sql.Date  是java.util.Date的子类
*        1、两个构造器的使用
*               构造器一:Date():创建一个对应当前时间的Date()对象
*               构造器二:创造指定毫秒数的Date对象
*        2、两个方法的使用:
*               toString():显示当前的年、月、日、时、分、秒
*               getTime():获取当前Date对象对应的毫秒数,(时间戳)

*         3、java.sql.Date()表示的是数据库中的时间

package DateTime;import java.util.Date;/*
*        java.util.Date
*               |---java.sql.Date  是java.util.Date的子类
*        1、两个构造器的使用
*               构造器一:Date():创建一个对应当前时间的Date()对象
*               构造器二:创造指定毫秒数的Date对象
*        2、两个方法的使用:
*               toString():显示当前的年、月、日、时、分、秒
*               getTime():获取当前Date对象对应的毫秒数,(时间戳)
*
*/
public class DateText {public static void main(String[] args) {//构造器一:Date():创建一个对应当前时间的Date()对象Date d1=new Date();//因为存在两个相同的类,所以不会自动导包,因此需要我们选择导包System.out.println(d1.toString());//Mon Oct 04 22:26:22 CST 2021System.out.println(d1.getTime());//1633357582399//构造器二:创造指定毫秒数的Date对象Date d2=new Date(1633357582399L);//记得要加L,因为数字可能会超出int类型的范围System.out.println(d2.toString());//Mon Oct 04 22:26:22 CST 2021System.out.println("**********************************");//将Java.util.Date对象转化为java.sql.Date对象//情况一:Date d3 =new java.sql.Date(1633357582399L);System.out.println(d3.toString());//2021-10-04java.sql.Date d4=(java.sql.Date)d3;//情况二:Date d5=new Date();java.sql.Date d7=new java.sql.Date(d5.getTime());System.out.println(d7.toString());}
}

*   本程序主要介绍的是SimpleDateFormat的使用:SimpleDateFormat和对日期Date类的格式化与解析
*   1、两个操作:
*       格式化:日期--->字符串
*       解析:格式化的逆过程:字符串--->日期
*   2、SimpleDateFormat的实例化

​
package DateTime;import org.junit.Test;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
2021 10.5*   本程序主要介绍的是SimpleDateFormat的使用:SimpleDateFormat和对日期Date类的格式化与解析
*   1、两个操作:
*       格式化:日期--->字符串
*       解析:格式化的逆过程:字符串--->日期
*   2、SimpleDateFormat的实例化public class Datetext1 {@Testpublic void text1() throws ParseException {默认构造器:构造一个 SimpleDateFormat使用默认模式和日期格式符号为默认的 FORMAT区域设置。SimpleDateFormat sdf=new SimpleDateFormat();格式化:日期--->字符串SimpleDateFormat对象的format方法将date对象的时间格式化:Wed Oct 06 16:03:56 CST 2021--->2021/10/6 下午4:03Date date=new Date();System.out.println(date);  Wed Oct 06 15:35:48 CST 2021String format=sdf.format(date);System.out.println(format);//2021/10/6 下午3:59解析:格式化的逆过程:字符串--->日期因为我们在创建池SimpleDateFormat对象的时候选择默认的构造器,所以也就是选择了默认的转化格式,也就是说这样的实例解析字符串的格式是默认的如果格式不对的话就会报错public Date parse(String source) throws ParseException 会抛出异常,所以要要导入java.text.ParseExceptionString str="2021/10/6 下午4:03";//这样的格式符合默认的构造器创建的对象Date date1=sdf.parse(str);System.out.println(date1);//Wed Oct 06 16:03:00 CST 2021/*String str1="2021-10-06 04:03"; 这样的就会报错,因为不符合该对象解析字符串的格式Date date2= sdf.parse(str1);*/在创建SimpleDateFormat的时候可以选择其他的构造器,然后就可以自己指定自己的对象的解析格式SimpleDateFormat mysdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");String str2="2021-10-06 16:5:23";//斜杆和冒号也是格式的一部分,不要忘记加哦Date date2=mysdf.parse(str2);System.out.println(date2);//Wed Oct 06 16:05:23 CST 2021}//练习:将“2021-02-08”转化为Date@Testpublic void text2() throws ParseException {SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");String str="2021-02-08";Date date=sdf.parse(str);System.out.println(date);//Mon Feb 08 00:00:00 CST 2021java.sql.Date date1=new java.sql.Date(date.getTime());//类型转化System.out.println(date1);//2021-02-08}
}

3、Calendar

*   本程序主要介绍的是日历类Calender
*        1、实例化
*        方式一:创建其子类(GregorianCalendar)的对象
*        Calendar cal1=new GregorianCalendar();
*        方式二:调用其静态方法getInstance()
*        Calendar calendar=Calendar.getInstance();//在创建的同时获取此时的时间 2021 10.6
*        2、常用方法
*        public int get(int field):参数列表是Calendar中的一个属性,例如:Calendar.DAY_OF_MONTH
*        public abstract void add(int field, int amount): 第一个参数是Calendar中的一个属性,例如:Calendar.DAY_OF_MONTH,
*        public void set(int field, int value):第一个参数是Calendar中的一个属性,例如:Calendar.DAY_OF_MONTH,作用是将该值改变为value
*        public final Date getTime():将日历类转化为Date
*        public final void setTime(Date date)

package DateTime;import org.junit.Test;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
//2021 10.6*   本程序主要介绍的是日历类Calender
*        1、实例化
*        方式一:创建其子类(GregorianCalendar)的对象
*        Calendar cal1=new GregorianCalendar();
*        方式二:调用其静态方法getInstance()
*        Calendar calendar=Calendar.getInstance();//在创建的同时获取此时的时间 2021 10.6
*        2、常用方法
*        public int get(int field):参数列表是Calendar中的一个属性,例如:Calendar.DAY_OF_MONTH
*        public abstract void add(int field, int amount): 第一个参数是Calendar中的一个属性,例如:Calendar.DAY_OF_MONTH,
*        public void set(int field, int value):第一个参数是Calendar中的一个属性,例如:Calendar.DAY_OF_MONTH,作用是将该值改变为value
*        public final Date getTime():将日历类转化为Date
*        public final void setTime(Date date)public class CalenderTest {@Testpublic void test1(){//1、实例化//方式一:创建其子类(GregorianCalendar)的对象Calendar cal1=new GregorianCalendar();//方式二:调用其静态方法getInstance()Calendar calendar=Calendar.getInstance();//在创建的同时获取此时的时间 2021 10.6//2、常用方法//get()//public int get(int field):参数列表是Calendar中的一个属性,例如:Calendar.DAY_OF_MONTHint days=calendar.get(Calendar.DAY_OF_MONTH);//我的理解就是获取了对象calendar的DAY_OF_MONTHSystem.out.println(days);//6//add()//public abstract void add(int field, int amount): 第一个参数是Calendar中的一个属性,例如:Calendar.DAY_OF_MONTH,//该方法的作用是使该属性改变amountcalendar.add(Calendar.DAY_OF_MONTH,+2);System.out.println(calendar.get(Calendar.DAY_OF_MONTH));//8Calendar calendar1=Calendar.getInstance();System.out.println(calendar1.get(Calendar.DAY_OF_MONTH));//6//set()//public void set(int field, int value):第一个参数是Calendar中的一个属性,例如:Calendar.DAY_OF_MONTH,作用是将该值改变为valuecalendar.set(Calendar.DAY_OF_MONTH,10);System.out.println(calendar.get(Calendar.DAY_OF_MONTH));//10//getTime():日历类--->Date//public final Date getTime():将日历类转化为DateDate time = calendar.getTime();System.out.println(time);//Sun Oct 10 22:07:58 CST 2021//setTime():Date--->日历类//public final void setTime(Date date)Date date=new Date();calendar.setTime(date);System.out.println(calendar.get(Calendar.DAY_OF_MONTH));//6System.out.println(calendar);//好家伙,直接输出全部信息//java.util.GregorianCalendar[time=1633529556118,areFieldsSet=true,areAllFieldsSet=true,// lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,// useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2021,// MONTH=9,WEEK_OF_YEAR=41,WEEK_OF_MONTH=2,DAY_OF_MONTH=6,DAY_OF_YEAR=279,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=1,// AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=12,SECOND=36,MILLISECOND=118,ZONE_OFFSET=28800000,DST_OFFSET=0]}
}

4、三个Local

*   本程序主要介绍的是:LocalDate、LocalTime、LocalDateTime三个类
*       创造对象的两种方法:
*           1、通过now()来获取当前时间,创造对象
*           2、通过of()加参数来构造对象
*       以下的方法三者基本都试用:
*           getXxx():得到数据
*           withXxx():将该数据改变并返回一个同类型的对象,但是原对象的数据不会发生改变--->这三个类具有不可变性
*           plusXxxx()  将某一个属性家加上某一个值   不可变性
*           minusXxxxx() 将某一个属性减去某个值,原对象的数据不会发生改变

package DateTime;import org.junit.Test;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
2021 10.7*   本程序主要介绍的是:LocalDate、LocalTime、LocalDateTime三个类
*       创造对象的两种方法:
*           1、通过now()来获取当前时间,创造对象
*           2、通过of()加参数来构造对象
*       以下的方法三者基本都试用:
*           getXxx()
*           withXxx():将该数据改变并返回一个同类型的对象,但是原对象的数据不会发生改变--->这三个类具有不可变性
*           plusXxxx()  将某一个属性家加上某一个值   不可变性
*           minusXxxxx() 将某一个属性减去某个值,原对象的数据不会发生改变public class LocalDateXXX {@Testpublic void test1(){//创造对象的两种方法://1、通过now()来获取当前时间,创造对象LocalDate now = LocalDate.now();LocalTime now1 = LocalTime.now();LocalDateTime now2 = LocalDateTime.now();System.out.println(now);//2021-10-07System.out.println(now1);//22:22:07.048921800System.out.println(now2);//2021-10-07T22:22:07.048921800//2、通过of()加参数来构造对象LocalDate of = LocalDate.of(2021, 10, 7);LocalTime of1 = LocalTime.of(22, 24, 20, 20);LocalDateTime of2 = LocalDateTime.of(2021, 10, 7, 22, 25, 22);System.out.println(of);//2021-10-07System.out.println(of1);//22:24:20.000000020System.out.println(of2);//2021-10-07T22:25:22//以下的方法三者基本都试用,但是以LocalDateTime的对象来做示范//getXxx()System.out.println(now2.getDayOfMonth());//7System.out.println(now2.getDayOfYear());//280System.out.println(now2.getHour());//22System.out.println(now2.getMonth());//OCTOBERSystem.out.println(now2.getMonthValue());//10//withXxx():将该数据改变并返回一个同类型的对象,但是原对象的数据不会发生改变--->这三个类具有不可变性LocalDateTime localDateTime = now2.withDayOfMonth(8);System.out.println(now2.getDayOfMonth());//7System.out.println(localDateTime.getDayOfMonth());//8//plusXxxx()  将某一个属性家加上某一个值   不可变性LocalDateTime localDateTime1 = now2.plusDays(1);System.out.println(localDateTime1.getDayOfYear());//281System.out.println(now2.getDayOfYear());//280//minusXxxxx() 将某一个属性减去某个值,原对象的数据不会发生改变LocalDateTime localDateTime2 = now2.minusDays(1);System.out.println(localDateTime2.getDayOfYear());//279System.out.println(now2.getDayOfYear());//280}
}

5、Instant

*   1、实例化
*        public static Instant now()  --->   Instant now = Instant.now();
*   2、常用方法
*        public OffsetDateTime atOffset(ZoneOffset offset):设置时间的偏移量
*        public long toEpochMilli():获取自1970年1月1日0时0秒(UTC)开始的毫秒数
*        public static Instant ofEpochMilli(long epochMilli):通过给定的毫秒数,获取Instant实例 等同于Date类的getTime()

package DateTime;import org.junit.Test;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Date;2021 10.8*  本程序主要介绍类Instant
*   1、实例化
*        public static Instant now()  --->   Instant now = Instant.now();
*   2、常用方法
*        public OffsetDateTime atOffset(ZoneOffset offset):设置时间的偏移量
*        public long toEpochMilli():获取自1970年1月1日0时0秒(UTC)开始的毫秒数
*        public static Instant ofEpochMilli(long epochMilli):通过给定的毫秒数,获取Instant实例 等同于Date类的getTime()public class InstantTest {@Testpublic void test1(){//实例化Instant now = Instant.now();System.out.println(now);//2021-10-08T05:06:38.932184600Z  因为获取的时间是本初子午线的时间// 而我们的时间是北京时间为东八区时间,相差了八个小时//添加时间的偏移量  整挺麻烦OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));System.out.println(offsetDateTime);//2021-10-08T13:09:13.627390600+08:00//public long toEpochMilli():获取自1970年1月1日0时0秒(UTC)开始的毫秒数long milli =now.toEpochMilli();System.out.println(milli);//1633669997634//public static Instant ofEpochMilli(long epochMilli):通过给定的毫秒数,获取Instant实例 等同于Date类的getTime()Instant instant = Instant.ofEpochMilli(1633669997634L);System.out.println(instant);//2021-10-08T05:13:17.634ZDate date=new Date(now.toEpochMilli());System.out.println(date);//Fri Oct 08 13:16:53 CST 2021}
}

6、DateTimeFormatter

*         本程序主要介绍的是DateTimeFormatter对三个Local时间类的格式化以及解析
 *             1、创造了分别对应三个类的对象DateTimeFormatter对象:
*                DateTimeFormatter format=DateTimeFormatter.ISO_LOCAL_DATE_TIME;
*                DateTimeFormatter format1=DateTimeFormatter.ISO_LOCAL_TIME;
*                DateTimeFormatter format2=DateTimeFormatter.ISO_LOCAL_DATE;
*            2、利用DateTimeFormatter对象的format()方法将对应的对象的数据转化为String  日期--->字符串
*            3、解析:字符串--->日期   但是字符串是有一定的格式的,解析的返回值类型为TemporalAccessor,如果使用强制类型转化向下转型就会报错    
*                本地的相关的格式:
*                    举例: 对于LocalDate的格式以及其输出结果:
*                        FormatStyle.FULL:2021年10月8日 星期五
*                        FormatStyle.LONG:2021年10月8日
*                        FormatStyle.MEDIUM: 2021-10-8
*                        FormatStyle.SHORT: 21-10-8
*                自定义解析格式:
*                    举例:ofPattern("yyyy-MM-dd hh:mm:ss");

package myPackage;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor;2021 10.8*       本程序主要介绍的是DateTimeFormatter对三个Local时间类的格式化以及解析*          1、创造了分别对应三个类的对象DateTimeFormatter对象:
*               DateTimeFormatter format=DateTimeFormatter.ISO_LOCAL_DATE_TIME;
*               DateTimeFormatter format1=DateTimeFormatter.ISO_LOCAL_TIME;
*               DateTimeFormatter format2=DateTimeFormatter.ISO_LOCAL_DATE;
*           2、利用DateTimeFormatter对象的format方法将对应的对象的数据转化为String  日期--->字符串
*           3、解析:字符串--->日期   但是字符串是有一定的格式的,解析的返回值类型为TemporalAccessor,如果使用强制类型转化向下转型就会报错
*               本地的相关的格式:
*                   举例: 对于LocalDate的格式以及其输出结果:
*                       FormatStyle.FULL:2021年10月8日 星期五
*                       FormatStyle.LONG:2021年10月8日
*                       FormatStyle.MEDIUM: 2021-10-8
*                       FormatStyle.SHORT: 21-10-8
*               自定义解析格式:
*                   举例:ofPattern("yyyy-MM-dd hh:mm:ss");
*
*public class DateTimeFormatTest {public static void main(String[] args) {//创造了一个DateTimeFormatter对象DateTimeFormatter format=DateTimeFormatter.ISO_LOCAL_DATE_TIME;DateTimeFormatter format1=DateTimeFormatter.ISO_LOCAL_TIME;DateTimeFormatter format2=DateTimeFormatter.ISO_LOCAL_DATE;//格式化:利用DateTimeFormatter对象的format方法将对应的对象的数据转化为String  日期--->字符串LocalDateTime localdatetime=LocalDateTime.now();String str1=format.format(localdatetime);System.out.println(localdatetime);//2021-10-08T15:18:10.037System.out.println(str1);//2021-10-08T15:18:10.037LocalTime localTime=LocalTime.now();String str2=format1.format(localTime);System.out.println(localTime);//15:22:16.062System.out.println(str2);//15:22:16.062LocalDate localDate=LocalDate.now();String str3=format2.format(localDate);System.out.println(localDate);//2021-10-08System.out.println(str3);//2021-10-08//解析:字符串--->日期   但是字符串是有一定的格式的TemporalAccessor parse=format.parse(str1);    //解析的返回值类型为TemporalAccessor,如果使用强制类型转化向下转型就会报错   System.out.println(parse);//{},ISO resolved to 2021-10-08T15:40:17.677//本地的相关格式:/*举例: 对于LocalDate的格式以及其输出结果:*      FormatStyle.FULL:2021年10月8日 星期五*     FormatStyle.LONG:2021年10月8日*     FormatStyle.MEDIUM: 2021-10-8*      FormatStyle.SHORT: 21-10-8*/DateTimeFormatter for0 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL);DateTimeFormatter for1=DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);DateTimeFormatter for2=DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);DateTimeFormatter for3=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);String DateStr0=for0.format(localDate);String DateStr1=for1.format(localDate);String DateStr2=for2.format(localDate);String DateStr3=for3.format(localDate);System.out.println(DateStr0);//2021年10月8日 星期五System.out.println(DateStr1);//2021年10月8日System.out.println(DateStr2);//21-10-8System.out.println(DateStr3);//2021-10-8System.out.println("*************************************");//自定义的格式化格式:ofPattern("yyyy-MM-dd hh:mm:ss");DateTimeFormatter datetimefaomatter1=DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");DateTimeFormatter datetimefaomatter2=DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss.E");String str4=datetimefaomatter1.format(LocalDateTime.now());System.out.println(str4);//2021-10-08 04:13:16//System.out.println(datetimefaomatter2.format(LocalTime.now()));System.out.println(LocalDateTime.now());//2021-10-08T16:17:43.811TemporalAccessor myparse=datetimefaomatter1.parse("2020-10-08 10:24:30");//小时数不能超过12,否则就会出现错误System.out.println(myparse);}
}

7、Comparable、Compartor

1、Comparable接口

*  本程序主要介绍的是Comparable接口的使用(自然排序):
*   1、像String、包装类等实现了Comparable接口。重写了compareTo()方法,给出了比较两个对象的方法
*   2、像String、包装类重写了compareTo()方法以后,进行了从小到大的排列
*   3、重写compareTo(obj)的规则:
*       如果当前对象this大于形参对象obj,则返回正整数
*       如果当前对象this小于形参对象obj,则返回负整数
*       如果当前对象this等于形参对象obj,则返回0
*   4、是需要被排序的类实现Comparable接口,然后重写compareTO()方法,然后再使用Arrays.sort()方法时会调用重写的该方法

package CompareTest;import java.util.Arrays;
2021 10.8*  本程序主要介绍的是Comparable接口的使用(自然排序):
*   1、像String、包装类等实现了Comparable接口。重写了compareTo()方法,给出了比较两个对象的方法
*   2、像String、包装类重写了compareTo()方法以后,进行了从小到大的排列
*   3、重写compareTo(obj)的规则:
*       如果当前对象this大于形参独享onj,则返回正整数
*       如果当前对象this小于形参独享onj,则返回负整数
*       如果当前对象this等于形参独享onj,则返回0
*   4、是需要被排序的类实现Comparable接口,然后重写compareTO()方法,然后再使用Arrays.sort()方法时会调用重写的该方法public class ComparableTEst {public static void main(String[] args) {Person1 arr[]=new Person1[4];arr[0]=new Person1(15,"CC");arr[1]=new Person1(23,"FF");arr[2]=new Person1(23,"EE");arr[3]=new Person1(24,"MM");Arrays.sort(arr);System.out.println(Arrays.toString(arr));//如果不重写类的toString方法,就会输出[CompareTest.Person1@6d311334, CompareTest.Person1@682a0b20,//CompareTest.Person1@3d075dc0, CompareTest.Person1@214c265e]Person2 p2=new Person2();System.out.println(p2.toString());// 如果没重写toString方法,则默认是Object的toString方法返回值:// package名称.类名@哈希玛值;CompareTest.Person2@6acbcfc0}}
//实现Comparable接口
class Person1 implements Comparable{int age;String name;public Person1(int age, String name) {this.age = age;this.name = name;}Person1(){}//如果不重写tiString方法,以便输出数组的时候调用@Overridepublic String toString() {return "Persom{" +"age=" + age +", name='" + name + '\'' +'}';}//重写compareTo方法@Overridepublic int compareTo(Object o) {System.out.println("调用compareTo方法");if(o instanceof Person1){Person1 p=(Person1)o;if(this.age>p.age){return 1;}else if(this.age<p.age){return -1;}else{return this.name.compareTo(p.name);}}else{throw new RuntimeException();}}
}
class Person2{}

2、Comparator

*   本程序主要介绍的是Comparator(定制排序):
*       1、当元素的类型没有实现Java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,此时就可以考虑使用Comparator的对象来排序.

一般使用匿名对象,但是匿名对象只能排列一次,如果想要对同一个类的数组进行多次相同规格的排列,就需要创造一个类,然后实现Comparator接口,并重写compare方法

package CompareTest;import org.junit.Test;import java.util.Arrays;
import java.util.Comparator;2021 10.9*   本程序主要介绍的是Comparator(定制排序):
*       1、当元素的类型没有实现Java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,
*       此时就可以考虑使用Comparator的对象来排序
*
*public class ComparatorTest {@Testpublic void test(){Person1 arr[]=new Person1[4];arr[0]=new Person1(15,"CC");arr[1]=new Person1(23,"CC");arr[2]=new Person1(23,"EE");arr[3]=new Person1(24,"MM");//方法一:使用匿名对象,只可以使用一次,在只排序一次的时候使用/*Arrays.sort(arr,new Comparator(){//先按姓名从小到大排序,然后使用年龄从大到小排序@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof Person1 && o2 instanceof Person1){Person1 p1=(Person1) o1;Person1 p2=(Person1) o2;if(p1.name.equals(p2.name)){//如果字符串不相等,那就执行elsereturn -Integer.compare(p1.age,p2.age);//相等的话就判断下一个,哈哈哈,无限套娃}else {return p1.name.compareTo(p2.name);}}throw new RuntimeException("输入的类型不匹配");}});*///方法二:搞出来一个实现了Comparator接口的类Arrays.sort(arr,new compareim());//同样的也会调用该类的compare方法System.out.println(Arrays.toString(arr));//[Persom{age=23, name='CC'}, Persom{age=15, name='CC'}, Persom{age=23, name='EE'}, Persom{age=24, name='MM'}]System.out.println(arr[0]);//默认调用toString方法}
}
class compareim implements Comparator{@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof Person1 && o2 instanceof Person1){System.out.println("调用了compare方法");Person1 p1=(Person1) o1;Person1 p2=(Person1) o2;if(p1.name.equals(p2.name)){//如果字符串不相等,那就执行elsereturn -Integer.compare(p1.age,p2.age);//相等的话就判断下一个,哈哈哈,无限套娃}else {return p1.name.compareTo(p2.name);}}throw new RuntimeException("输入的类型不匹配");}
}

十、枚举类和注解

1、枚举类的含义

1、枚举类的理解:类的对象个数只有有限个,是确定的,我们称此类为枚举类
        2、当需要定义一组常量时,强烈建议使用枚举类
        3、如果枚举类只有一个对象,则可以作为单例模式的实现方式

2、枚举类的定义

方式一:自定义枚举类

package Meijulei;*   本程序主要介绍的是枚举类:
*   一、枚举类的使用:
*       1、枚举类的理解:类的对象个数只有有限个,是确定的,我们称此类为枚举类
*       2、当需要定义一组常量时,强烈建议使用枚举类
*       3、如果枚举类只有一个对象,则可以作为单例模式的实现方式
*   二、如何定义枚举类
*       方式一:自定义枚举类
*           1、声明Season对象的属性:private final修饰
*           2、私有化类的构造器,并给对象属性赋值
*           3、提供当前枚举类的多个对象:public static final修饰
*           4、其他诉求1:获取枚举类对象的属性
public class EnumTest1 {public static void main(String[] args) {Season season=Season.AUTUMN;//芜湖,原来如此System.out.println(season);//Season{seasonName='秋天', seasonDesc='秋高气爽'}}}
//自定义枚举类
class Season{//1、声明Season对象的属性:private final修饰private final String seasonName;private final String seasonDesc;//2、私有化类的构造器,并给对象属性赋值private Season(String seasonName,String seasonDesc){this.seasonName=seasonName;this.seasonDesc=seasonDesc;}//3、提供当前枚举类的多个对象:public static final修饰public static final Season SPRING =new Season("春天","春暖花开");public static final Season SUMMER =new Season("夏天","夏日炎炎");public static final Season AUTUMN =new Season("秋天","秋高气爽");public static final Season WINTER =new Season("冬天","凛冬将至");//4、其他诉求1:获取枚举类对象的属性public String getSeasonName(){return this.seasonName;}public String getSeasonDesc(){return seasonDesc;}//其他诉求2:@Overridepublic String toString() {return "Season{" +"seasonName='" + seasonName + '\'' +", seasonDesc='" + seasonDesc + '\'' +'}';}}

方式二:使用enum关键字定义枚举类

*   本程序主要介绍的是enum定义的枚举类
*       1、利用enum代替class修饰类,那么该类就是枚举类
*       2、首先列出枚举类的对象,多个对象之间用“,”隔开。不用写public static final,直接写枚举类的名称和参数就可以
*       3、私有化类的构造器,并给对象属性赋值
*   常用方法:
*       values():获取枚举类中的所有变量,并作为数组返回
*       valueOf(String str):返回带指定名称的指定枚举类型的枚举常量。
*   注意:
*       1、在没有重写toString方法的时候枚举类的名称。如果是Object的子类,那么应该输出的是地址值,但是输出的不是地址值,说明该类不是Object的子类
*       2、该类的父类是java.lang.Enum
*       3、非通过enum定义的枚举类无法使用枚举类的方法

*   注意:
*       1、在没有重写toString方法的时候枚举类的名称。如果是Object的子类,那么应该输出的是地址值,但是输出的不是地址值,说明该类不是Object的子类
*       2、该类的父类是java.lang.Enum
*       3、非通过enum定义的枚举类无法使用枚举类的方法
*   枚举类实现接口:
*       情况一:跟其他类相似
*       情况二:每个枚举对象都对方法进行实现,然后不同的枚举对象的该方法都不一样

package Meijulei;
2021 10.9
2021 10.10*   本程序主要介绍的是enum定义的枚举类
*       1、利用enum代替class修饰类,那么该类就是枚举类
*       2、首先列出枚举类的对象,多个对象之间用“,”隔开。
*       3、私有化类的构造器,并给对象属性赋值
*   常用方法:
*       values():获取枚举类中的所有变量,并作为数组返回
*       valueOf(String str):返回带指定名称的指定枚举类型的枚举常量。
*   注意:
*       1、在没有重写toString方法的时候枚举类的名称。如果是Object的子类,那么应该输出的是地址值,但是输出的不是地址值,说明该类不是Object的子类
*       2、该类的子类是java.lang.Enum
*       3、非通过enum定义的枚举类无法使用枚举类的方法
*   枚举类实现接口:
*       情况一:跟其他类相似
*       情况二:每个枚举对象都对方法进行实现,然后不同的枚举对象的该方法都不一样
*public class EunmTest {public static void main(String[] args) {Season1 season=Season1.SPRING;在没有重写toString方法的时候枚举类的名称。如果是Object的子类,那么应该输出的是地址值,但是输出的不是地址值,说明该类不是Object的子类System.out.println(season);寻找该类的父类,可知该类的子类是java.lang.EnumSystem.out.println(Season1.class.getSuperclass());//class java.lang.EnumSeason1 arr[]=Season1.values();values():获取枚举类中的所有变量,并作为数组返回for(Season1 x:arr){System.out.println(x);x.show();}//SPRING SUMMER AUTUMN WINTER 每两个之间有一个换行System.out.println("*****************************");Thread.State arr1[]=Thread.State.values();//内部类for(Thread.State x:arr1){System.out.println(x);}//进程的几种状态:NEW RUNNABLE BLOCKED WAITING TIMED_WAITING TERMINATEDvalueOf():返回带指定名称的指定枚举类型的枚举常量。Season1 s=Season1.valueOf("WINTER");System.out.println(s);//WINTER//Season1 s1=Season1.valueOf("WINTE");//如果没有与字符串匹配的枚举类,那么就会报错:IllegalArgumentException(参数不合法)// System.out.println(s1);}}
interface A{void show();
}
enum Season1 implements A{1、提供当前枚举类的对象,多个对象之间用“,”隔开。情况二:每个枚举对象都对方法进行实现,然后不同的枚举对象的该方法都不一样SPRING ("春天","春暖花开"){@Overridepublic void show() {System.out.println("这是春天!");}},SUMMER ("夏天","夏日炎炎"){@Overridepublic void show() {System.out.println("这是夏天!");}},AUTUMN ("秋天","秋高气爽"){@Overridepublic void show() {System.out.println("这是秋天!");}},WINTER("冬天","凛冬将至"){@Overridepublic void show() {System.out.println("这是冬天!");}};2、声明Season对象的属性:private final修饰private final String seasonName;private final String seasonDesc;3、私有化类的构造器,并给对象属性赋值private Season1(String seasonName,String seasonDesc){this.seasonName=seasonName;this.seasonDesc=seasonDesc;}4、其他诉求1:获取枚举类对象的属性public String getSeasonName(){return this.seasonName;}public String getSeasonDesc(){return seasonDesc;}//其他诉求2:@Overridepublic String toString() {return "Season{" +"seasonName='" + seasonName + '\'' +", seasonDesc='" + seasonDesc + '\'' +'}';}第一种情况,该情况下的每个枚举对象的方法都是一样的/*  @Overridepublic void show() {System.out.println("现在是某个季节!!!");}*/
}
enum Son{EYE(2);private final int name;Son(int i) {name=i;}
}

十一、集合

1、集合框架的概述

1.集合、数组都是对多个数据进行存储操作的结构,简称Java容器。说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(. txt,.jpg,.avi,数据库
        2.1数组在存储多个数据方面的特点:
                >一旦初始化以后,其长度就确定了
                >数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了
                比如: String[] arr;int[] arr1;object[] arr2;
        2.2数组在存储多个数据方面的缺点:
                >一-旦初始化以后,其长度就不可修改。
                >数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
                >获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
                >数组有储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

2、关于集合的接口以及实现类

|----Collection 接口:单列集合,用于储存一个一个的对象。

|----LIst接口:存储有序的、可重复的数据(动态数组)

|----ArraysList、LinkedLIst、Vector    <------三个实现类

|----Set接口:存储无序的、不可重复的数据

|----HashSet、LinkedHashSet、TreeSet  <----三个实现类

|----Map接口:双列集合,用来储存一对(key---value)一对的数据

|----HashMap、LinkedHashMap、TreeMap、Properties  <----四个实现类

3、collection接口中的常用方法

1、添加
        ➢add(Object obj)
        ➢addAll(Collection coll)
2、获取有效元素的个数
        ➢int size()
3、清空集合
        ➢void clear()
4、是否是空集合
        ➢boolean isEmpty()
5、是否包含某个元素
        ➢ boolean contains(Object obj):是通过元素的equals方法来判断是否是同一个对象、contains方法比较的时候调用的是对象的equals方法。这就要求元素所属的类需要重写equlas方法。

➢boolean containsAll(Collection c): 也是调用元素的equals方法来比较的。拿两个集合的元素挨个比较。

6、删除
        ➢boolean remove(Object obj) :通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第-一个元素
        ➢boolean removeAll(Collection coll):取当前集合的差集
7、取两个集合的交集
        ➢boolean retainAIl(Collection c): 把交集的结果存在当前集合中,不影响c . A.retainAll(B),A调用这个方法之后,集合A中只剩下存在于B中的元素,返回值为false表示集合A没改变,返回true集合A发生改变

8、集合是否相等
        ➢boolean equals(Object obj)
9、转成对象数组
        ➢Object[] toArray()
10、获取集合对象的哈希值
        ➢hashCode()
11、 遍历
        ➢iterator(): 返回迭代器对象,用于集合遍历

利用迭代器Iterator来遍历Collection集合

三个方法:

 boolean hasNext():就像一个角标,从集合中的第一位之前开始,一直判断下一位是否有元素,如果有元素的话就会返回一个true.但是如果不调用next()方法的话,角标不会向后移动 *                  next():返回集合角标所在位置的元素,每调用一次角标自然向后移动一位,如果想要多次遍历集合,那么就要对迭代器的对象进行重置

remove():删除集合中的一个元素,但是该方法是Iterator类的方法,不是集合类的方法

每一个Collection对象都可以通过iterator()方法来得到一个Iterator类的实例:

Iterator iterator = c1.iterator()

package MyCollection;
/*
*   本程序旨在通过迭代器iterator来遍历Collection集合
*            boolean hasNext():就像一个角标,从集合中的第一位之前开始,一直判断下一位是否有元素,如果有元素的话就会返回一个true.但是如果不调用
*                               next()方法的话,角标不会向后移动
*            next():返回集合角标所在位置的元素,每调用一次角标自然向后移动一位,如果想要多次遍历集合,那么就要对迭代器的对象进行重置
*            remove():删除集合中的一个元素,但是该方法是Iterator类的方法,不是集合类的方法
*/
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;public class IteratorTest {public static void main(String[] args) {Collection c1=new ArrayList();for (int i = 0; i < 6; i++) {c1.add(i);}System.out.println(c1);//[0, 1, 2, 3, 4, 5]//每次迭代都需要重新设置迭代器的值Iterator iterator = c1.iterator();
//        这里角标越界也会报错:NoSuchElementExceptionfor (int i = 0; i < c1.size() ; i++) {//0 1 2 3 4 5System.out.print(iterator.next()+" ");}System.out.println();Collection c2=new ArrayList();for (int i = 0; i < 6; i++) {c2.add(i);}Iterator iterator1 = c2.iterator();while(iterator1.hasNext()){//0 1 2 3 4 5System.out.print(iterator1.next()+" ");}}@Testpublic void test1(){Collection c1=new ArrayList();for (int i = 0; i < 6; i++) {c1.add(i);}System.out.println(c1);//[0, 1, 2, 3, 4, 5]Iterator iterator = c1.iterator();while(iterator.hasNext()){if(iterator.next().equals(1)){iterator.remove();}}System.out.println(c1);//[0, 2, 3, 4, 5]}@Testpublic void test4(){
//        如果调用next()方法,hasNext()方法的角标不会向后移动Collection c1=new ArrayList();for (int i = 0; i < 6; i++) {c1.add(i);}System.out.println(c1);//[0, 1, 2, 3, 4, 5]Iterator iterator = c1.iterator();int i=0;while(iterator.hasNext()) {i++;if(i>10){break;}}System.out.println(iterator.next());}
}

4、Collection的实现类

|----Collection 接口:单列集合,用于储存一个一个的对象。

|----LIst接口:存储有序的、可重复的数据(动态数组)

|----ArraysList:作为List接口的主要实现类:线程不安全,效率高;底层使用Object[] elementDate存储

|----LinkedLIst:对于频繁的插入、删除操作,使用此类效率比ArraysList高;底层使用双向链表存储

|----Vector   :作为List接口的古老实现类;线程安全,效率低;底层使用Object[] elementData存储

1、ArraysList类

底层通过Object[] elementDate存储

无参构造器: ArraysList list=new ArraysList()

 jdk1.7之前都是直接默认创建一个容量为10的数组,每次需要扩容的时候都是扩容为原来容量的1.5倍。jdk1.8开始无参构造器没有创建底层数组,直到使用add()方法添加数据的时候才会进行创建,默认容量也是10,每次扩容也是变为原来容量的1.5倍

建议直接使用带参的构造器: ArraysList list=new ArraysList(int capacity)

ArraysList类和数组一样,但是却有很多的方法供我们使用

2、List接口的常用方法

 void add(int index, Object ele): 在index位置插入eLe元素
        boolean addAll(int index, Collection eles): M从index位置开始将eles中的所有元素添加进来
        0bject get(int index): 获取指定index位置的元素
        int indexOf(object obj): 返回obj在集合中首次出现的位置
        int LastIndex0f(Object obj):返回obj在当前集合中末次出现的位置
        Object remove(int index): 移除指定index位置的元素,并返回此元素
        object Set(int index, object ele): 设置指定index位置的元素为ele
        List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合

      List中的元素的类需要重写equals()方法,便于remove()等其他方法使用

3、Set接口

 Set接口的框架:
        |----Collection接口:单列集合,用来存储-一个一个的对象
                |---Set接口: 存储无序的、不可重复的数据--> 高中讲的“集合”
                        |----HashSet: 作为Set接口的主要实现类;线程不安全的;可以存储null值
                                |----LinkedHashSet: 作为HashSet的于类; 遍历其内部数据时,可以按照添加的顺序存储
                        |----TreeSet:可以按照添加对象的指定属性,进行排序。

  Set接口特点

无序性:  数组中存储的顺序不是按照元素添加的顺序,而是根据元素的hashCode的值来确定元素的角标。

不可重复性:在数组中不可以存在完全相同的两个元素

  Set接口中的元素的类需要重写equals()和hashCode()方法

添加元素的过程:以HashSet为例:
我们向HashSet中添加元素a,首先调用元素a所在的类的hashCode()方法,计算元素a的哈希值,接着利用这个哈希值通过某种算法计算出该元素a在HashSet底层数组中的存放位置(索引位置)。判断此数组此位置上是否已经有元素
            如果此数组该位置没有元素,那么就将元素a放在该位置。--->情况1
            如果此数组此位置已经有了元素b(或者存在链表),那么就需要判断元素a和元素b的哈希值
                        如果哈希值不相同,那么元素a添加成功--->情况2
                        如果哈希值相同,那么就需要调用元素a的equals()方法:
                                    如果equals()方法返回true,说明元素a和元素b完全相同,元素a添加失败
                                    如果equals()方法返回false,元素a添加成功。---->情况3

对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上的数据以链表的方式存储。
jdk1.8之后,原来的元素的数组中,新添加的元素在链表的后面
也就是说HashSet底层的存储方式是:数组加链表

在向HashSet的集合中添加元素的时候,元素所属的类需要重写euqals()和hashCode()方法

Set接口没有添加新的抽象方法,他的方法与Collection一样。

LinkedHashSet也是无序的,它可以选择指向前一个元素和后一个元素,因此在遍历的时候可以通过元素的指向来寻找下一个元素,也就实现了顺序遍历

4、TreeSet

TreeSet,只能添加相同类的元素,不可以添加相同的元素,而且顺序默认为从小到达的顺序
*       关于如何去重和排序
*           1、如果添加的元素的类实现类Comparable接口,那么就用该类的compare方法进行去重(排序)
*           2、如果没有是实现该接口,那么就用TreeSet构造方法:TreeSet treeSet=new TreeSet(new Comparator())传进来的Comparator的进行去重(排序)

package MyCollection;*  TreeSet:只能添加相同类的元素,不可以添加相同的元素,而且顺序默认为从小到达的顺序
*       关于如何去重和排序
*           1、如果添加的元素的类实现类Comparable接口,那么就用该类的compare方法进行去重(排序)
*           2、如果没有是实现该接口,那么就用TreeSet构造方法:TreeSet treeSet=new TreeSet(new Comparator())
*           传进来的Comparator的进行去重(排序)
*import com.sun.source.tree.Tree;
import org.junit.Test;import java.util.Comparator;
import java.util.Iterator;
import java.util.Objects;
import java.util.TreeSet;public class TreesetTest {@Testpublic void test1(){TreeSet treeSet = new TreeSet();treeSet.add(1);//TreeSet只能添加相同类的对象
//        treeSet.add("2");treeSet.add(5);treeSet.add(3);treeSet.add(6);treeSet.add(2);System.out.println(treeSet);// 从小到大[1, 2, 3, 5, 6]}@Testpublic void test2(){TreeSet treeSet = new TreeSet();treeSet.add("A");treeSet.add("M");treeSet.add("D");treeSet.add("E");Iterator iterator = treeSet.iterator();while(iterator.hasNext()){System.out.print(iterator.next()+" ");//A D E M}}@Testpublic void test3(){TreeSet treeSet = new TreeSet();treeSet.add(new Son("Jack",5));treeSet.add(new Son("LC",6));treeSet.add(new Son("Ja",10));treeSet.add(new Son("Mike",5));treeSet.add(new Son("Mike",20));Iterator iterator = treeSet.iterator();while(iterator.hasNext()){System.out.println(iterator.next()+" ");}System.out.println("A".compareTo("A"));
//        TreeSet比较相同与否是通过元素的compareTo()方法来进行比较的,如果我们的compareTo()方法值比较了name属性,那么底5个元素和第4元素compareTo
//        的返回值为0,也就是说他们是一样的,所以就会添加失败,然后输出下面的内容/*   Son{name='Ja', age=10}Son{name='Jack', age=5}Son{name='LC', age=6}Son{name='Mike', age=5}在我们更改了元素的compareTo()方法,使得元素的每一个属性都会得到比较,这样才可以添加成功Son{name='Ja', age=10}Son{name='Jack', age=5}Son{name='LC', age=6}Son{name='Mike', age=5}Son{name='Mike', age=20}
*/}@Testpublic void test4(){Comparator com=new Comparator(){@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof Son&&o2 instanceof Son){Son s1=(Son)o1;Son s2=(Son)o2;int num1=s1.getName().compareTo(s2.getName());if(num1!=0){return  num1;}else{return Integer.compare(s1.getAge(),s2.getAge());}}else{throw new RuntimeException("类型不匹配");}}};TreeSet treeSet = new TreeSet();treeSet.add(new Son("Ja",5));treeSet.add(new Son("LC",6));treeSet.add(new Son("Ja",10));treeSet.add(new Son("Mike",5));treeSet.add(new Son("Mike",20));Iterator iterator = treeSet.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
class Son implements Comparable{private String name;private int age;public Son() {}public Son(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;}@Overridepublic String toString() {return "Son{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Son son = (Son) o;return age == son.age && Objects.equals(name, son.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic int compareTo(Object o) {if(o instanceof Son){Son s=(Son)o;int num= this.name.compareTo(s.name);if(num!=0){//不相同return num;}else{// name相同,判断agereturn Integer.compare(this.age,s.age);}}else{throw new RuntimeException("数据类型不匹配");}}
}

5、集合与数组之间的转化

集合---->数组   :public Object[] toArray()

数组---->集合   :public static <T> List<T> asList

类的数组转化为集合会将数组中的每一个元素都转化为集合中的每一个元素,而基本数据类型的数组会转化为集合中的一个元素(该元素为一个数组)

package MyCollection;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*
*  本程序旨在介绍数组与集合之间的相互转化
*/
public class Transfor {public static void main(String[] args) {ArrayList arrayList = new ArrayList();for (int i = 0; i < 6; i++) {arrayList.add(i);}System.out.println(arrayList);
//        集合转化为数组Object arr[]=arrayList.toArray();for (Object x: arr) {System.out.print(x+" ");}System.out.println();
//        数组转化为集合//类的数组转化为集合会将数组中的每一个元素都转化为集合中的每一个元素List arrayList1= Arrays.asList(new String[]{"AAA","bbb","ccc"});System.out.println(arrayList1);System.out.println( arrayList1.size());//3//而基本数据类型的数组会转化为集合中的一个元素(该元素为一个数组)List<int[]> ints = Arrays.asList(new int[]{1, 2, 3, 4});System.out.println(ints);//[[I@3d075dc0]System.out.println(ints.size());//1List<boolean[]> booleans = Arrays.asList(new boolean[]{true, false});System.out.println(booleans);//[[Z@214c265e]System.out.println(booleans.size());//1}
}

5、Map

1、Map的结构

*   |----Map:双列数据,存储key-value对的数据
*           |---- HashMap:作为Map的主要实现类:线程不安全,效率高;可以存储null的key和value
*                   |----LinkedHashMap:保证在遍历Map元素的时候,可以按照添加的顺序进行遍历
*                           原因:在原有的HashMap底层的结构基础上,添加了一对指针,指向前一个和后一个元素,对于频繁的遍历操作,此类执行效率高于HashMap
*           |---- TreeMap:保证按照添加的key-value对进行排序,是实现排序遍历。此时考虑对于key的自然排序或者定制排序(排序的实现类似于Treeset);底层使用红黑树
*           |---- Hashtable:作为古老的实现类:线程安全的,效率低的;不能存储null的key和value
*                   |---- Properties:常用来处理配置文件。key和value都是String类型

HashMap的底层:数组+链表+红黑树

Map中的value:无序的,可重复的,可以使用Collection存储所有的value --->value所在的类要重写equals() , 在寻找value对应的Entry的时候可以直接先通过key的hashCode来寻找,然后再通过value的值寻找,所以value不需要重写hashCode() *

一个键值对:key-value构成了一个Entry对象 *

Map中的entry:无序的、不可重复的,使用Set存储所有的Entry

2、Map的底层原理:

*       HashMap map = new HashMap() :
*       在实例化以后底层没有创建数组,在调用put()添加元素之后,底层才创建了长度是16的一维数组Node[] table。
*       ...可能已经执行过多次put...
*       map. put (key1, vaLue1):
*       首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry 数组中的存放位置。
*           如果此位置上的数据为空,此时的key1-value1添加成功。---- 情况1
*           如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
*               如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1 添加成功。---- 情况2
*               如果key1的哈希值和已经存在的某一个数据(key2-value2) 的哈希值相同,继续比较:调用key1所在类的equals(key2)
*                   如果equals()返回false:此时key1-value1添加成功。---- 情况3
*                   如果equals()返回true:使用value1替换vaLue2。
*   补充:  关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
*   在不断的添加过程中,会涉及到扩容问题
(当达到临界值且存放的位置非空),默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
*   存储结构:数组+链表+红黑树---->当数组上的某个索引位置上的元素以链表形式存在的数据数据个数>8,且当前数组长度大于64的时候,此时此索引位置上的所有的数据改为使用红黑树存储
*   DEFAULT_ INITIAL_ CAPACITY : HashMap 的默认容量,16
*   DEFAULT_ LOAD_ _FACTOR: HashMap 的默认加载因子: 0.75
*   threshold:扩容的临界值,=容量*填充因子: 16 * 0.75 => 12
*           该值如果太小的话,可能对数组的利用率不够,但是如果该值太大,那么就会导致链表的数量过多
*   TREEIFY_THRESHOLD: Bucket 中链表长度大于该默认值,转化为红黑树:8
*   MIN_TREEIFY_CAPACITY: 桶中的Node 被树化时最小的hash表容量:64

3、Map集合的常用方法

添加、删除、修改操作:
object put(object key, object value): 将指定key-vaLue添加到(或修改)当前map对象中
void putALL(Map m): 将m中的所有key-value对存放到当前map中
object remove(0bject key): 移除指定key的key-value对,并返回value
void clear():清空当前map中的所有数据,不是把Map对象设置为null

元素查询的操作:
object get(Object key):获取指定key对应的vaLue
boolean containsKey(Object key):是否包含指定的key
boolean containsValue (Object value): 是否包含指定的value
int size(): 返回map中key-value对的个数
boolean isEmpty(): 判断当前map是否为空
boolean equals(Object obj):判断当前map和参数对象obj是否相等  参数为Map,且存储的内容一样的时候才是true

4、遍历Map

遍历的目的在于拿到数据 :
*       Set keySet(): 返回所有key构成的Set集合
*       Collection values(): 返回所有value构成的Collection集合
*       Set entrySet(): 返回所有key-value对构成的Set集合

package Map;import org.junit.Test;import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;*  本程序旨在测试遍历Map集合的几种方法,遍历的目的在于拿到数据:
*       Set keySet(): 返回所有key构成的Set集合
*       Collection values(): 返回所有value构成的Collection集合
*       Set entrySet(): 返回所有key-value对构成的Set集合public class BIianli {@Testpublic void test1(){HashMap hashMap = new HashMap();for(int i=0;i<4;i++){hashMap.put(i,i+1);}
方法一://通过keySet()方法将key值返回到一个Set数组之中,遍历Set set = hashMap.keySet();Iterator iterator = set.iterator();while(iterator.hasNext()){//拿到key值Object key=iterator.next();//通过key值和get方法 拿到value值Object value=hashMap.get(key);System.out.println(key+"---->"+value);}
方法二:System.out.println("????");//通过entrySet()方法得到元素为Entry类型的Set集合Set set1 = hashMap.entrySet();Iterator iterator1 = set1.iterator();while(iterator1.hasNext()){Object obj=iterator1.next();HashMap.Entry e=(HashMap.Entry)obj;System.out.println(e.getKey()+"--->"+e.getValue());}//遍历valueCollection values = hashMap.values();Iterator iterator2 = values.iterator();while(iterator2.hasNext()){Object obj=iterator2.next();System.out.print(obj+" ");}}
}

6、Collections---->集合的工具类

常用方法:

    public static void reverse(List<?> list):反转List中元素的顺序
    shuffLe(List):对List集合元素进行随机排序
    sort(List):根据元素的自然顺序对指定List集合元素按升序排序,需要元素所属的类实现Comparable接口
    sort(List, Comparator): 根据指定的Comparator产生的顺序对List集合元素进行排序
    swap(List, int i,int j):将指定List集合中的i处元素和j处元素进行交换

    Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
    Object max(Collection, Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
    Object min(Collection)
    Object min(Collection, Comparator)
    int frequency(Collection, object):返回指定集合中指定元素的出现次数
    void copy(List dest,List src): 将src 中的内容复制到dest中
    boolean replaceAll(List list, object oldVal, object newVal): 使用新值替换List对

copy方法的使用:

//该测试主要测试的copy方法@Testpublic void test3(){ArrayList list = new ArrayList();for (int i = 0; i < 4; i++) {list.add(i);}System.out.println(list);//[0, 1, 2, 3]List list1=new ArrayList();Collections.copy(list1,list);System.out.println(list1);java.lang.IndexOutOfBoundsException: Source does not fit in dest 出现错误因为copy方法要求第一个List的size要大于等于第二个List的size这种写法也会出现错误,因为参数是list1的底层数组elementData的长度,实际list1的size仍是0List list1=new ArrayList(list.size()+1);Collections.copy(list1,list);System.out.println(list1);*/所以需要这样写List list1= Arrays.asList(new Object[list.size()]);System.out.println(list1.size());//4Collections.copy(list1,list);System.out.println(list1);//[0, 1, 2, 3] Yes!}
}

十二、IO流

1、File类

  File的作用是指明流操作的文件

1、File类的构造器

public File(String pathname)

        public File(String parent, String child) 第一个是父文件所在的目录,后一个是子文件

        public File(File parent, String child)

Window系统下的文件分隔符为”\\"

package MyIO;import org.junit.Test;import java.io.File;/*
* 本程序主要介绍的是File的构造方法
*/
public class FileConstructor {@Testpublic void test1(){构造器1 public File(String pathname)File file1 = new File("Hello.txt");相对路径:1、使用单元测试方法:相对于我们这module 也就是说这个文件在module下()2、使用main方法File file2=new File("D:\\code\\JAVA\\first_java_project\\IO\\Hello.txt");输出的是地址pathSystem.out.println(file1);//Hello.txtSystem.out.println(file2);//D:\code\JAVA\first_java_project构造器2:public File(String parent, String child)  第一个是父文件所在的目录,后一个是子文件File file3=new File("D:\\code\\JAVA\\first_java_project\\IO","Hello.txt");System.out.println(file3);//D:\code\JAVA\first_java_project\IO\Hello.txt构造器3:public File(File parent, String child)File file4=new File(file3,"myHello.txt");System.out.println(file4);//D:\code\JAVA\first_java_project\IO\Hello.txt\myHello.txt}
}

2、File的使用及其一些方法

* File的使用:
*   1、File类的一个对象,代表一个文件或一个文件目录(文件夹)
*   2、File类声明在java.io包下
*   3、File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,但是并未涉及到写入或者读取文件内
*   的操作。如果需要读写或写入文件内容,必须使用IO流来完成
*   4、后续File类的对象常作为参数传递到流的构造器中,指明读写或写入的终点
*
*  本程序主要介绍的是File类的一些方法
public String getAbsolutePath(): 获取绝对路径
public String getPath() : 获取路径
public String getName() : 获取名称
public String getParent(): 获取上层文件目录路径。若无,返回null
public Long Length() :获取文件长度(即:字节数)。不能获取目录的长度。
public long LastModified() :获取最后一次的修改时间,毫秒值
如下的两个方法适用于文件目录:
public String[] list() :获取指定目录下的所有文件或者文件康录的名称数组
public File[] ListFiles() :获取指定目录下的所有文件或者文件目录的File数组

*
*  以下方法不做示范:
public boolean isDirectory():判断是否是文件目录
public boolean isFile():判断是否是文件
public boolean exists():判断是否存在

public boolean canRead() :判断是否可读
public boolean canWrite() :判断是否可写.
public boolean isHidden() :判断是否隐藏(我也想知道文件怎么隐藏,狗头)

package MyIO;import org.junit.Test;import java.awt.print.Book;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;* File的使用:
*   1、File类的一个对象,代表一个文件或一个文件目录(文件夹)
*   2、File类声明在java.io包下
*   3、File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,但是并未涉及到写入或者读取文件内
*   的操作。如果需要读写或写入文件内容,必须使用IO流来完成
*   4、后续File类的对象常作为参数传递到流的构造器中,指明读写或写入的终点
*
*  本程序主要介绍的是File类的一些方法
public String getAbsolutePath(): 获取绝对路径
public String getPath() : 获取路径
public String getName() : 获取名称
public String getParent(): 获取上层文件目录路径。若无,返回null
public Long Length() :获取文件长度(即:字节数)。不能获取目录的长度。
public long LastModified() :获取最后一次的修改时间,毫秒值
如下的两个方法适用于文件目录:
public String[] list() :获取指定目录下的所有文件或者文件康录的名称数组
public File[] ListFiles() :获取指定目录下的所有文件或者文件目录的File数组
*
*  以下方法不做示范:
public boolean isDirectory():判断是否是文件目录
public boolean isFile():判断是否是文件
public boolean exists():判断是否存在
public boolean canRead() :判断是否可读
public boolean canWrite() :判断是否可写.
public boolean isHidden() :判断是否隐藏public class FileMethod {@Testpublic void test1(){//file1文件已经存在File file1=new File("mytest.txt");System.out.println(file1.getAbsoluteFile());//D:\code\JAVA\first_java_project\IO\mytest.txtSystem.out.println(file1.getPath());//mytest.txtSystem.out.println(file1.getName());//mytest.txtSystem.out.println(file1.getParent());//nullSystem.out.println(file1.length());//0字节大小System.out.println(file1.lastModified());//1636115261329System.out.println(new Date(file1.lastModified()));//Fri Nov 05 20:27:41 CST 2021System.out.println("************************************");//file2文件其实并不存在 但是以下操作并不会抛出异常File file2=new File("mytest1.txt");System.out.println(file2.getAbsoluteFile());//D:\code\JAVA\first_java_project\IO\mytest1.txtSystem.out.println(file2.getPath());//mytest1.txtSystem.out.println(file2.getName());//mytest1.txtSystem.out.println(file2.getParent());//nullSystem.out.println(file2.length());//0字节大小System.out.println(file2.lastModified());//0File file4=new File("D:\\code\\JAVA\\first_java_project");for(String x: file4.list()){System.out.println(x);}
//        打印该目录下的全部文件或目录的名称/*.idea2012080070_李军宝2012080070_李军宝.zipBookCollectionfirst_java_project.imlGenericityIOMeijulei1myfirst.propertiesoutsrcUsefulClass*/
//        打印的更详细了for(File x: file4.listFiles()){System.out.println(x);}/*D:\code\JAVA\first_java_project\.ideaD:\code\JAVA\first_java_project\2012080070_李军宝D:\code\JAVA\first_java_project\2012080070_李军宝.zipD:\code\JAVA\first_java_project\BookD:\code\JAVA\first_java_project\CollectionD:\code\JAVA\first_java_project\first_java_project.imlD:\code\JAVA\first_java_project\GenericityD:\code\JAVA\first_java_project\IOD:\code\JAVA\first_java_project\Meijulei1D:\code\JAVA\first_java_project\myfirst.propertiesD:\code\JAVA\first_java_project\outD:\code\JAVA\first_java_project\srcD:\code\JAVA\first_java_project\UsefulClass*/}@Testpublic void test2(){//public boolean renameTo(File dest):将file路径下的文件转移到dest的路径下,并将文件名修改为dest的文件名,需要this存在,dest不存在File file1=new File("mytest.txt");//存在File file2=new File("myhello1.txt");//不存在System.out.println( file2.renameTo(file1));//false 操作失败System.out.println(file1.renameTo(file2));//true 操作成功 不存在mytest.txt 存在mytest1.txt(第一次执行会成功,//此操作并不局限于一个文件夹下的文件}//public boolean createNewFile():创建文件,若此文件已经存在,则不创建。返回false 上级目录需要存在//public boolean mkdir():创建文件目录。如果此文件目录已经存在,就不会创建。如果此文件的上层目录不存在也不会进行创建//public boolean mkdirs():如果上级目录不存在的话,上级目录也会被创建//public boolean delete(): 删除文件或者文件目录@Testpublic void test3() throws IOException {
//       在该module创建文件File file=new File("file1");System.out.println(file.exists());//falsefile.createNewFile();System.out.println(file.exists());//trueFile file1=new File("D:\\code\\JAVA\\first_java_project\\file1");System.out.println(file1.exists());//falsefile1.createNewFile();System.out.println(file1.exists());}
}

2、流的分类

1、操作数据单位:字节流、字符流

对于文本文件(.txt,.java,.c,.cpp)使用字符流来进行操作

对于非文本文件(.jpg,.mp3,.avi,.doc)使用字节流来进行操作

2、数据的流向:输入流、输出流
        3、流的角色:节点流、处理流

3、流的体系结构

缓冲流的方法跟前面节点流的方法一样,不再赘述

抽象基类 节点流 缓冲流
InputStream FileInputStream  (read(byte[] buffer) BufferedInputStream
OutputStream FileOutputStream (write( byte[] buffer,0,len)

BufferOutpputStream

Reader FileReader (read( char[] input) BUfferedReader (readLine())
Writer FileWriter (write (char[] input ,0.len) BufferedWriter 

1、流的操作步骤

1、创建File对象

2、以FIle 对象为参数创建流对象

3、具体的流操作

4、关闭流

2、从文件中读取的操作示例(FIleReader):

注意:

*   1、要求读入的文件存在 否则会出现FileNotFoundException
*   2、read():返回读到的字符,如果到文件末尾就返回-1
*   3、在使用完毕之后一定要将流关闭 否则会造成资源的浪费

package MyIO;
//2021 11.10
import org.junit.Test;import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;*   1、要求读入的文件存在 否则会出现FileNotFoundException
*   2、read():返回读到的字符,如果到文件末尾就返回-1
*   3、在使用完毕之后一定要将流关闭 否则会造成资源的浪费public class FileRead {@Testpublic void test1() {FileReader read1= null;try {1、实例化File类的对象,指明需要操作的文件File file=new File("file11");2、提供操作的流对象read1 = new FileReader(file);//(1 如果此处出现异常 那么read1=null 会导致后边在关闭流的时候空指针异常3、读入数据//read():返回读到的字符,如果到文件末尾就返回-1int ifo=read1.read();while(ifo!=-1){//I do not want to continue.System.out.print((char)ifo);ifo=read1.read();}} catch (IOException e) {e.printStackTrace();} finally {4、关闭流if(read1!=null) {// 解决空指针异常try {read1.close();} catch (IOException e) {e.printStackTrace();}}}}@Testpublic void test2() {FileReader read= null;try {File file=new File("file1");read = new FileReader(file);char[] getIfo=new char[6];int len;//在内容充足的情况下 读取的字符个数取决于字符串数组的容量//从输出结果可以看出来,在读内容的时候字符数组的内容一直在该改变,如果最后读取的内容不够改变数组所有位置上的内容的话,那么就会优先填充//角标靠前的位置上的内容,而没有改变的内容则保持上一次读取的时候的内容。while((len=read.read(getIfo))!=-1){/*       错误写法for(int i=0;i<getIfo.length;i++){System.out.print(getIfo[i]);}123456  789456正确写法一:仅仅改变了遍历数组的位置,最后一次只遍历读取的内容大小的数组位置for(int i=0;i<len;i++){System.out.print(getIfo[i]);}//123456  789错误写法String str=new String(getIfo);System.out.print(str);123456  789456*///            正确写法String str=new String(getIfo,0,len);System.out.print(str);//123456  789System.out.print("  ");}} catch (IOException e) {e.printStackTrace();} finally {if(read!=null) {try {read.close();} catch (IOException e) {e.printStackTrace();}}}}}

3、写入文件的操作示例(FileWriter):

注意:

写入流的构造器如果file的路径文件不存在的话,那么就会直接在该路径创建该文件
            最后一个参数决定了是覆盖还是增加。
                如果不写该参数,那么每次写入的时候都会创建一个新的文件然后去覆盖掉原来的文件
                如果写了该参数
                    true:就会在原内容下扩增
                    false:就会创建新文件然后去覆盖掉原文件

package MyIO;
//2021 11.10
import org.junit.Test;import java.io.File;
import java.io.FileWriter;
import java.io.IOException;*   1、创建File对象
*   2、以File对象为参数创建写入流对象
*   3、写入的操作
*   4、关闭流public class MyFileWriter {@Testpublic void test1() {FileWriter writer= null;try {File file=new File("MyFileWriter.txt");写入流的构造器如果file的路径文件不存在的话,那么就会直接在该路径创建该文件最后一个参数决定了是覆盖还是增加。如果不写该参数,那么每次写入的时候都会创建一个新的文件然后去覆盖掉原来的文件如果写了该参数true:就会在原内容下扩增
/                   false:就会创建新文件然后去覆盖掉原文件writer = new FileWriter(file,true);String output1="I want to play games!!!\n";String output2="But I know I should study...wuwuwu";writer.write(output1);writer.write(output2);} catch (IOException e) {e.printStackTrace();} finally {if(writer!=null)try {writer.close();} catch (IOException e) {e.printStackTrace();}}}
}

4、读入并写出的流操作以及关闭两个流的示例:

package MyIO;
//2021 11.10
//本程序为一个从文件读取然后写入另一个文件的代码
//对于文本文件(.txt,.java,.c,.cpp)使用字符流来进行操作
//对于非文本文件(.jpg,.mp3,.avi,.doc)使用字节流来进行操作
import org.junit.Test;import java.io.*;public class ReadAndWrite {@Testpublic void test1() {FileReader read1= null;FileWriter write= null;try {File file1=new File("MyFileWriter.txt");File file2=new File("File2.txt");read1 = new FileReader(file1);write = new FileWriter(file2);char[] input=new char[5];int len;while((len=read1.read(input))!=-1){write.write(input,0,len);}} catch (IOException e) {e.printStackTrace();} finally {if(read1!=null) {try {read1.close();} catch (IOException e) {e.printStackTrace();}}if(write!=null) {try {write.close();} catch (IOException e) {e.printStackTrace();}}}}
}

5、使用字节流去操作图片和视频的示例:

package MyIO;
//  本程序旨在用字节流去复制图片
import org.junit.Test;import javax.annotation.processing.Filer;
import java.io.*;//  本程序旨在复制图片以及视频,仅仅使用字节流
public class CopyPicture {@Testpublic void test1(){FileInputStream input = null;FileOutputStream output = null;try {File file1 = new File("p.png");File file2 = new File("p1.png");input = new FileInputStream(file1);output = new FileOutputStream(file2);byte[] byteArray = new byte[6];int len;while((len=input.read(byteArray))!=-1){output.write(byteArray,0,len);}} catch (IOException e) {e.printStackTrace();} finally {if(input!=null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}if(output!=null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}}}//将复制封装到一个函数里面public void copy(String fromPath,String destPath){FileInputStream input = null;FileOutputStream output = null;try {File file1 = new File(fromPath);File file2 = new File(destPath);input = new FileInputStream(file1);output = new FileOutputStream(file2);byte[] byteArray = new byte[11];int len;while((len=input.read(byteArray))!=-1){output.write(byteArray,0,len);}} catch (IOException e) {e.printStackTrace();} finally {if(input!=null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}if(output!=null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}}}@Testpublic void test2(){long start = System.currentTimeMillis();String str="C:\\Users\\毛豆的花生\\Desktop\\1.mp4";String str2="C:\\Users\\毛豆的花生\\Desktop\\3.mp4";copy(str,str2);long end = System.currentTimeMillis();System.out.println("复制用的时间是"+(end-start));//复制用的时间是373}
}

6、使用缓冲流复制文件

package MyIO;import org.junit.Test;import java.io.*;//本程序为非文本文件的复制的代码,其中使用了字节流和缓冲流
/*
*  1、缓冲流:
*       BufferedInputStream
*       BufferedOutputStream
*       BufferedReader
*       BufferedWriter
*  2、作用: 提高流的读写速度
*           提高速度的原因:内部提供了一个缓冲区,加快了运输的效率
*/
public class CopyByBudffer {@Testpublic void test1()  {BufferedInputStream bi= null;BufferedOutputStream bo= null;try {//1、创建File对象File file1=new File("p.png");File file2=new File("p2.png");//2、创建流FileInputStream fi=new FileInputStream(file1);FileOutputStream fo=new FileOutputStream(file2);//3、创建缓冲流对象bi = new BufferedInputStream(fi);bo = new BufferedOutputStream(fo);//4、具体操作byte[] arr=new byte[11];int len;//由缓冲流来进行读写操作while((len=bi.read(arr))!=-1){bo.write(arr,0,len);}} catch (IOException e) {e.printStackTrace();} finally {//5、关闭缓冲流,关闭缓冲的同时也关闭了字节流if(bi!=null) {try {bi.close();} catch (IOException e) {e.printStackTrace();}}if(bo!=null) {try {bo.close();} catch (IOException e) {e.printStackTrace();}}}}public void copy(String from,String dest){BufferedInputStream bi= null;BufferedOutputStream bo= null;try {//1、创建File对象File file1=new File(from);File file2=new File(dest);//2、创建流FileInputStream fi=new FileInputStream(file1);FileOutputStream fo=new FileOutputStream(file2);//3、创建缓冲流对象bi = new BufferedInputStream(fi);bo = new BufferedOutputStream(fo);//4、具体操作byte[] arr=new byte[11];int len;//由缓冲流来进行读写操作while((len=bi.read(arr))!=-1){bo.write(arr,0,len);}} catch (IOException e) {e.printStackTrace();} finally {//5、关闭缓冲流,关闭缓冲的同时也关闭了字节流if(bi!=null) {try {bi.close();} catch (IOException e) {e.printStackTrace();}}if(bo!=null) {try {bo.close();} catch (IOException e) {e.printStackTrace();}}}}@Testpublic void test2(){String from="p.png";String dest="p3.png";long start= System.currentTimeMillis();copy(from,dest);long end=System.currentTimeMillis();System.out.println("复制花费的时间为:"+(end-start));//23//相比只用字节流去复制,使用了缓冲流的速度明显变快}@Testpublic void test3(){BufferedWriter bo= null;BufferedReader bi= null;try {/* File file1=new File("C:\\Users\\毛豆的花生\\Desktop\\1.txt");File file2=new File("2.txt");FileWriter write=new FileWriter(file2);FileReader read=new FileReader(file1);bo = new BufferedWriter(write);bi = new BufferedReader(read);*/bo=new BufferedWriter(new FileWriter(new File("3.txt")));bi=new BufferedReader(new FileReader(new File("2.txt")));/*  方法一:char[] arr=new char[1024];int len;while((len=bi.read(arr))!=-1){bo.write(arr,0,len);}*///方法二:String input;//选用readLine方法直接读取一行的内容,但是不包括换行符,因此需要我们自己添加换行符while((input=bi.readLine())!=null){
//                bo.write(input+"\n");//第一种方式bo.write(input);bo.newLine();//第二种方式}} catch (IOException e) {e.printStackTrace();} finally {if(bi!=null) {try {bo.close();} catch (IOException e) {e.printStackTrace();}}if(bi!=null) {try {bi.close();} catch (IOException e) {e.printStackTrace();}}}}}

7、打印流

package MyIO;import org.junit.Test;import java.io.*;//本程序主要测试的是处理流*  处理流可以直接从文件中读取特定类型的数据
*   DataInputStream和DataOutputStream分别套在InputStream和OutputStream的子类流上
*public class DataInputStreamTest {@Testpublic void test1() throws IOException {DataOutputStream da=new DataOutputStream(new FileOutputStream("DataOutput.txt"));只可以写入基本数据类型
//        da.(new Person("junbao",20));da.writeInt(10);da.flush();da.writeBoolean(true);da.flush();da.writeUTF("junbao");da.flush();}@Test从文件中读取数据 要按输入的顺序读取public void test2()  {DataInputStream da= null;try {da = new DataInputStream(new FileInputStream("DataOutput.txt"));int num1=da.readInt();boolean b1=da.readBoolean();String str=da.readUTF();System.out.println(num1);System.out.println(b1);System.out.println(str);} catch (IOException e) {e.printStackTrace();} finally {if(da!=null) {try {da.close();} catch (IOException e) {e.printStackTrace();}}}}
}

8、对象流

* 对象流传输的对象所在的类需要满足一下要求:
 *     1、实现接口:Serializable
 *     2、提供一个全局常量:private static final long serialVersionUID
 *     3、如果该类的属性里面存在其他的类对象,那么该对象所在的类也需要满足这些要求。(默认情况下,基本数据类型是可以序列化的)

对象流传输对象的前提是该对象所在的类是实现了Serializable接口(可序列化),并且提供private static final long serialVersionUID
     * 该常量不提供也可能正常运行,但是可能会出现错误:
     *     如果我们没有提供该常量的话,那么他的值就是Java在运行时会根据类的内部细节自动生成一个,如果类的内部结构发生了变化的话,那么他的serialVersionUID也会发生变化。这个时候在我们对写入的文件进行
     * 读取的时候就会出现错误。---->我的理解:就好像字符集一样,写入文件的时候没有指定serialVersionUID,然后系统根据我们的类给我们默认指定了一个字符集a,然后我们写入之后,我们又对该类进行了修改
     * 比如添加或者删除属性,之后系统又对该类的字符集进行修改,修改为b。此时我们读取的时候就去该类此时的字符集去读取该类在文件中的数据。此时的字符集为b,我们用字符集去解码用字符集a编码的数据,此时就会
     * 发生错误,因此我们应该指定serialVersionUID,那么在读取的时候就不会发生错误
     * 如果是在写入之后又对该类添加了某个属性,那么在读取的时候该类新添加的属性的值为默认值(null,0...)
    需要注意的是:ObjectOutputStream不能序列化static和transient修饰的属性,无论你有没有赋值,在序列化之后他们的值为默认值

package henu;import org.junit.Test;import java.io.*;本程序介绍的是对象流:ObjectStream、ObjectOutputStream* 对象流传输的对象所在的类需要满足一下要求:*     1、实现接口:Serializable*     2、提供一个全局常量:private static final long serialVersionUID*   3、如果该类的属性里面存在其他的类对象,那么该对象所在的类也需要满足这些要求。(默认情况下,基本数据类型是可以序列化的)* * 对象流传输对象的前提是该对象所在的类是实现了Serializable接口(可序列化),并且提供private static final long serialVersionUID* 该常量不提供也可能正常运行,但是可能会出现错误:*     如果我们没有提供该常量的话,那么他的值就是Java在运行时会根据类的内部细节自动生成一个,如果类的内部结构发生了变化的话,那么他的serialVersionUID也会发生变化。这个时候在我们对写入的文件进行* 读取的时候就会出现错误。---->我的理解:就好像字符集一样,写入文件的时候没有指定serialVersionUID,然后系统根据我们的类给我们默认指定了一个字符集a,然后我们写入之后,我们又对该类进行了修改* 比如添加或者删除属性,之后系统又对该类的字符集进行修改,修改为b。此时我们读取的时候就去该类此时的字符集去读取该类在文件中的数据。此时的字符集为b,我们用字符集去解码用字符集a编码的数据,此时就会* 发生错误,因此我们应该指定serialVersionUID,那么在读取的时候就不会发生错误* 如果是在写入之后又对该类添加了某个属性,那么在读取的时候该类新添加的属性的值为默认值(null,0...)* * 需要注意的是:ObjectOutputStream不能序列化static和transient修饰的属性,无论你有没有赋值,在序列化之后他们的值为默认值public class HelloWorld {@Testpublic void test1()  {ObjectOutputStream obj= null;try {// public ObjectOutputStream(OutputStream out) throws IOExceptionobj = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\obj.txt"));//将Person的对象写入obj.txtobj.writeObject(new Person("junbao",20));} catch (IOException e) {e.printStackTrace();} finally {if(obj!=null) {try {obj.close();} catch (IOException e) {e.printStackTrace();}}}}@Test//从文件中读取对象public void test2()  {ObjectInputStream obj= null;try {obj = new ObjectInputStream(new FileInputStream("obj.txt"));Person p=(Person)obj.readObject();System.out.println(p);//Person{name='junbao', age=20}} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {if(obj!=null){try {obj.close();} catch (IOException e) {e.printStackTrace();}}}}}class Person implements Serializable{private static final long serialVersionUID = -6060343040263809614L;private String name;private int age;public Person(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;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}

9、RandomAccesFile

package henu;import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.RandomAccess;import org.junit.Test;public class RandomStreamTest1 {@Testpublic void test1() {RandomAccessFile ra = null;RandomAccessFile ra1=null;try {//第二个参数是读写权限设置ra = new RandomAccessFile("C:\\Users\\Administrator\\Desktop\\1.png","r" );//文件不存在的时候自动创建ra1 = new RandomAccessFile("C:\\Users\\Administrator\\Desktop\\2.png","rw");byte[] b=new byte[1024];int len;while((len=ra.read(b))!=-1){ra1.write(b);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{if(ra!=null){try {ra.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if(ra1!=null){try {ra1.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}//写出的时候文件覆盖@Testpublic void test2() {RandomAccessFile ra=null;try {ra = new RandomAccessFile("C:\\Users\\Administrator\\Desktop\\my.txt", "rw");ra.write("acb".getBytes());} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{if(ra!=null)try {ra.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}

9、RandomAccessFile

●创建RandomAccessFile类灾例需要指定一个mode参数,该参数指
定RandomAccessFile的访问模式:
    ➢r:以只读方式打开
    ➢rw:扣开以便读取和写入
    ➢rwd:打开以便读取和写入;同步文件内容的更新
    ➢rws:打开以便读取和写入;同步文件内容和元数据的更新
    如果模式为只读r。则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。如果 模式为rw读写。
如果文件不存在则会去创建文件,如果存在则不会创建。

seek(long num):定位到某一个位置

package MyIO;import org.junit.Test;import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;//11.20
//本程序旨在介绍随机处理文件流的使用:●创建RandomAccessFile类灾例需要指定一个mode参数,该参数指
定RandomAccessFile的访问模式:➢r:以只读方式打开➢rw:扣开以便读取和写入➢rwd:打开以便读取和写入;同步文件内容的更新➢rws:打开以便读取和写入;同步文件内容和元数据的更新如果模式为只读r。则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。如果 模式为rw读写。
如果文件不存在则会去创建文件,如果存在则不会创建。public class RandomAccessFileTest {@Testpublic void test1()  {RandomAccessFile read= null;RandomAccessFile write= null;try {read = new RandomAccessFile("p.png","r");write = new RandomAccessFile("p8.png","rw");byte arr[]=new byte[1024];int len;while((len=read.read(arr))!=-1){write.write(arr,0,len);}} catch (IOException e) {e.printStackTrace();} finally {if(read!=null){try {read.close();} catch (IOException e) {e.printStackTrace();}}if(write!=null){try {write.close();} catch (IOException e) {e.printStackTrace();}}}}@Testpublic void test2(){byte[] arr="abc".getBytes(StandardCharsets.UTF_8);for(byte x : arr){System.out.println((char)x);//97 98 99  a b c}}@Test//该测试为从中间任意位置覆盖写入public void test3() {RandomAccessFile ra= null;try {ra = new RandomAccessFile("Random.txt","rw");byte[] arr=new byte[10];int len;//将写入的位置调整到角标位4的地方,然后覆盖写入ra.seek(4);ra.write("xyz".getBytes(StandardCharsets.UTF_8));} catch (IOException e) {e.printStackTrace();} finally {if(ra!=null){try {ra.close();} catch (IOException e) {e.printStackTrace();}}}}@Test//该测试是为了实现插入写入。思路就是先将插入位置后后面的数据用保存下来(StringBuffer),然后从该位置插入数据,然后再将保存下来的数据写入public void test4() throws IOException {RandomAccessFile ra=new RandomAccessFile("Random.txt","rw");ra.seek(3);StringBuffer str=new StringBuffer((int)new File("Random.txt").length());byte[] arr=new byte[10];int len;while((len=ra.read(arr))!=-1){str.append(new String(arr,0,len));}System.out.println(str);ra.seek(3);ra.write("xyz".getBytes(StandardCharsets.UTF_8));}
}

JAVA-暑假笔记(源自尚硅谷Java教程)相关推荐

  1. (尚硅谷java零基础教程)学习笔记day7/8-数组

    1.数组的概述 1.1 定义 数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理. 1.2 数组的相关概念 数组名 元素 数组的索引 ...

  2. java培训班教程视频最新尚硅谷Java培训全套教程

        尚硅谷Bank项目–0 5.尚硅谷全套JAVA教程–面试阶段–0 4.尚硅谷全套JAVA教程–JavaEE阶段–0 3.尚硅谷全套JAVA教程–JavaWEB阶段–0 2.尚硅谷全套JAVA教 ...

  3. 【程序源代码】每日种草-尚硅谷Java学科全套教程

    关键字:<我是种草囤货君.每日种草系列> 资源包 正文 | 内容 01 - [概述] "野火烧不尽,春风吹又生",最近小编我的种草欲总是挥之不去,不管别人劝阻还是自我放 ...

  4. 尚硅谷Java、HTML5前端、全栈式开发视频

    Java基础阶段: 一.20天横扫Java基础(课堂实录) https://pan.baidu.com/s/1htTzZRQ 二.尚硅谷Java基础实战--Bank项目 http://pan.baid ...

  5. 【视频分享】尚硅谷Java视频教程_Shiro视频

    群里小伙伴想要Shiro方面的视频,刚好看过尚硅谷的,还行,入门算不错的,这里找来分享给大家,周末好好学习~ Apache Shiro 是目前使用率较高的一个 Java 安全框架.本视频基于 Shir ...

  6. Java基础学习:尚硅谷项目三 开发团队调度软件

    Java基础学习:尚硅谷项目三 开发团队调度软件 一.软件功能与结构设计 1. 软件功能 该软件实现以下功能: 软件启动时,根据给定的数据创建公司部分成员列表(数组) 根据菜单提示,基于现有的公司成员 ...

  7. 【视频分享】尚硅谷Java视频教程_Spring Boot视频教程(下)整合篇

    尚硅谷Java视频教程_Spring Boot视频教程(下)整合篇 巅峰之作,全网仅此一套,再无企及! SpringBoot是企业级开发的整体整合解决方案,特别用于快速构建微服务应用,旨在用最简单的方 ...

  8. 尚硅谷 java基础第二个项目之客户关系管理系统

    尚硅谷 java基础第二个项目之客户关系管理系统. 做了一些完善,增加性别,电话,邮箱有效性验证.其中电话和邮箱验证直接"饮用"了网友的果汁. 在此感谢各位原著大佬们的分享. 具体 ...

  9. 尚硅谷Java入门视频教程导读及第一章

    尚硅谷Java入门视频教程导读及第一章 JAVA基础学习导读-编程入门 0.1概述 0.2 计算机硬件介绍 中央处理器(CPU) 存储设备 内存 比特(bit)和字节(byte) 内存 输入和输出设备 ...

最新文章

  1. 接口性能优化技巧,干掉慢代码!
  2. 工具类软件操作手册_北京数字化的金蝶云团队-北京金普蝶软件科技有限公司...
  3. fastapi 模式的额外信息,示例 / Cookie参数 / Header参数
  4. 质量码_在验证牛顿第二定律实验为什么要保证槽码质量m远远小于小车质量M?...
  5. as3中splice和slice的用法
  6. hdu 4055 hdu 4489 动态规划
  7. 幼师学计算机心得体会怎么写,幼儿教师学习心得
  8. Hibernate 原生SQL多表查询时-字段名相同-查询数据覆盖问题
  9. js分享微信 ,微博 ,qq空间
  10. 计算机考研专业课王道,王道论坛,专注于计算机考研的点点滴滴!
  11. 图解十大经典机器学习算法
  12. 20种水彩画笔效果PS笔刷
  13. NR SRB and message transfer
  14. Win 10 下无法安装.net framework 3.5,错误代码0x800F081F的解决方案
  15. Android平台epub阅读器推荐
  16. Coggle专访系统之神与我同在:我的竞赛学习路线
  17. Unity烘焙基础操作
  18. TCP/TP OSI7层模型和TCP/TP4层模型
  19. 直播APP搭建好之后,该如何运营呢
  20. python战反爬虫:爬取猫眼电影数据 (一)(Requests, BeautifulSoup, MySQLdb,re等库)

热门文章

  1. 【JAVASE】抽象类
  2. 北航操作系统课程-20200227课堂小测-操作系统引论
  3. 推广提升页面转化率的9个技巧,get效果翻倍!
  4. (转)黑客组织敲诈袭击新浪、QQ等多家国内网站
  5. 网络爬虫Crawler~~~~python 爬虫~~~省市区~~抓~~快递公司~多线程
  6. Zed-Unity插件中代码注释——ZEDManager.cs
  7. #今日论文推荐#ECCV 2022 | 清华字节提出MGD:适用于分类/检测/分割的生成式知识蒸馏
  8. centos离线下载依赖rmp包
  9. 精益生产对成本管理有什么影响?
  10. 科普:身份证号为什么不会重复?