JAVA基础学习
-
随便建一个文件夹,存放代码
-
新建一个java文件,文件后缀名为.java
-
Hello.java
-
-
编写代码
public class Hello{
public static void main(String[] args){
System.out.print("Hello World!");
}
}
-
cmd编译javac java文件,会生成一个class文件,例如:javac Hello.java
-
运行class文件,java class文件,例如:java Hello
可能遇到的问题
-
每个单词的大小写不能出现问题,java是大小写敏感的
-
尽量使用英文;
-
文件名和类名必须保证一致,并且首字母大写
-
符号使用了中文
IDEA安装
-
-
找到developer tools选中IDEA
-
下载Community免费版
-
![]()
-
一直下一步
JAVA基础语法
注释
-
单行注释://
-
多行注释:/**/
-
文档注释:
/** *我是文档注释 */
-
选中代码ctrl+/
Java标识符命名
-
首字母只能是大小写字母以及美元符$或者下划线_开始
数据类型
-
强类型语言:要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用(安全)
-
弱类型语言:与强类型语言相反
-
数据类型:
-
八大基本类型:byte 1字节,short 2字节,int 4字节,long 8字节,float 4字节,double 8字节,char 2字节,boolean
-
引用类型:类,接口,数组
-
-
整数扩展:进制 二进制前面+0b 十进制 八进制前面+0 十六进制前面+0x
-
浮点数拓展?银行业务怎么表示?钱? //BigDecimal 数学工具类 //最好完全避免使用浮点数进行比较
-
//所有的字符本质还是数字
-
boolean flag=true; if(flag==true){} 两个相等 if(flag){}
类型转换
-
从低到高:byte-->short-->char-->int-->long-->float-->double
-
//强制转换 (类型)变量名 高-->低 /*自动转换 低-->高 例: int i=128; double d=i; */
-
/* 注意点: 1.不能对布尔值进行转换 2.不能把对象类型转换为不相干的类型 3.在把高容量转换到低容量的时候,强制转换 4.转换的时候可能存在内存溢出,或者精度问题! */
变量
-
类变量:在类下,static修饰
-
局部变量:方法里,必须声明和初始化
-
实例变量:在类下,从属于对象,如果不自行初始化,为这个类型的默认值
-
布尔值默认是false
-
除了基本类型,其余的默认值都是null
变量的命名规范
-
变量与方法名:首字母小写和驼峰原则:monthSalary
-
类名:首字母大写和驼峰原则:GoodMan
-
常量名一般用大写字符。
常量
-
final修饰,值被设定后,在程序运行过程中不允许被改变。
-
常量名一般用大写字符。
-
final修饰的类不可以被继承。
-
final修饰的方法不可以重写。
运算符
-
![]()
-
public static void main(String[] args) { long a=1212112112L; int b=123; short c=10; byte d=8; System.out.println(a+b+c+d);//long System.out.println(b+c+d);//int System.out.println(c+d);//int } -
public static void main(String[] args) { //关系运算符返回的结果:正确,错误 布尔值 int a=10; int b=20; int c=21; //取余,模运算 System.out.println(c%a);//c/a 21/10=2...1 System.out.println(a>b);//false System.out.println(a<b);//true System.out.println(a==b);//false System.out.println(a!=b);//true } -
//二元运算符 public static void main(String[] args) { //二元运算符 //Ctrl+D:复制当前行到下一行 int a=10; int b=20; int c=30; int d=40; System.out.println(a+b); System.out.println(a-b); System.out.println(a*b); System.out.println(a/(double)b); } -
//一元运算符 public static void main(String[] args) { //++自增 都是一元运算符 //--自减 int a=3; int b=a++;//a++执行完这行代码后,先给b赋值,再自增 System.out.println(a);//4 int c=++a;//++a执行完这行代码前,先自增,再给b赋值 System.out.println(a);//5 System.out.println(b);//3 System.out.println(c);//5 //幂运算2^3 2*2*2=8 double pow=Math.pow(2,3); System.out.println(pow); } -
//逻辑运算符 public static void main(String[] args) { //与(and) 或(or) 非(取反) boolean a=true; boolean b=false; System.out.println("a && b:"+(a && b));//逻辑与运算,两个变量为真,结果才为true System.out.println("a || b:"+(a || b));//逻辑或运算,两个变量有一个为真,结果就为true System.out.println("!(a && b):"+!(a && b));//如果是真,则变为假,如果是假就变为真 //短路运算 int c=5; boolean d=(c<4)&&(c++<4); System.out.println(d);//false System.out.println(c);//c=5 } -
//位运算 public static void main(String[] args) { /*位运算 A=0011 1100 B=0000 1101 A&B 0000 1100 A|B 0011 1101 A^B 0011 0001 ~B 1111 0010 2*8=16 2*2*2*2 效率极高!!! << *2 >> /2 0000 0000 0 0000 0001 1 0000 0010 2 0000 0011 3 0000 0100 4 0000 1000 8 0001 0000 16 */ System.out.println(2<<3); } -
public static void main(String[] args) { int a=10; int b=20; a+=b;//a=a+b; //a-=b;//a=a-b; System.out.println(a);//30 //字符串连接符 + ,String System.out.println(a+b); System.out.println(""+a+b);//3020 System.out.println(a+b+"");//50 }
包机制
-
一般利用公司域名倒置作为包名:com.baidu.www
JavaDoc
-
@author 作者名
-
@version版本名
-
@since指名需要最早使用的jdk版本
-
@param参数名
-
@return返回值情况
-
@throws异常抛出情况
-
进入命令提示符=>找到HelloWorld.java文件=>javadoc -encoding UTF-8 -charset UTF-8 HelloWorld.java
Java流程控制
Scanner
public static void main(String[] args) {
//输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并输出执行结果。
Scanner scanner=new Scanner(System.in);
//和
double sum=0;
//计算输入了多少个数字
int m=0;
//通过循环判断是否还有输入,并在里面通过每一次进行求和统计
while (scanner.hasNextDouble()){
double x=scanner.nextDouble();
m=m+1;//m++
sum=sum+x;
System.out.println("你输入了第"+m+"个数据,然后当前结果sum="+sum);
}
System.out.println(m+"个数的和为"+sum);
System.out.println(m+"个数的平均值是"+(sum/m));
scanner.close();
}
顺序结构
public static void main(String[] args) {
System.out.println("hello1");
System.out.println("hello2");
System.out.println("hello3");
System.out.println("hello4");
System.out.println("hello5");
System.out.println("hello6");
}
选择结构
-
//if-else if-else public static void main(String[] args) { //考试分数大于60就是及格,小于60分就不及格 Scanner scanner=new Scanner(System.in); System.out.println("请输入成绩:"); int score=scanner.nextInt(); if(score==100){ System.out.println("恭喜满分"); }else if(score<100&&score>=90){ System.out.println("A级"); }else if(score<90&&score>=80){ System.out.println("B级"); }else if(score<80&&score>=70){ System.out.println("C级"); }else if(score<70&&score>=60){ System.out.println("D级"); }else if(score<60&&score>=0){ System.out.println("不及格"); }else{ System.out.println("成绩不合法!"); } scanner.close(); } -
switch case语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
-
byte,short,int,char,String
-
case标签必须为字符串常量或字面量。
-
//switch
public static void main(String[] args) {
String name="GX";
//反编译 java---class(字节码文件)----反编译(IDEA)
switch (name){
case "jj":
System.out.println("jj");
break;
case "GX":
System.out.println("GX");
break;
default:
System.out.println("弄啥捏!");
}
}
While循环
//while 1到100的和
public static void main(String[] args) {
int i=0;
int sum=0;
while(i<=100){
sum=sum+i;
i++;
}
System.out.println(sum);
}
do...while
-
循环体至少会执行一次
-
//do...while public static void main(String[] args) { int a=0; while (a<0){ System.out.println(a); a++; }//没有输出 System.out.println("================="); do { System.out.println(a); a++; }while (a<0);//0 }
for循环
-
是最有效、最灵活的循环结构
-
//初始化//条件判断//迭代 for(int i=1;i<=100;i++){ System.out.println(i); } System.out.println("for循环结束!"); //死循环 for(;;){ } -
//练习1:计算0到100之间的奇数和,偶数的和 public static void main(String[] args) { int oddSum=0; int evenSum=0; for (int i = 0; i <=100; i++) { if(i%2!=0){ oddSum+=i; }else{ evenSum+=i; } } System.out.println("奇数的和:"+oddSum); System.out.println("偶数的和:"+evenSum); } -
//练习2:用while或for循环输出1-1000之间能被5整除的数,并且每行输出3个 // for (int i = 1; i <=1000; i++) { // if(i%5==0){ // System.out.print(i+"\t"); // } // if(i%(5*3)==0){//换行 // System.out.println(); // //System.out.print("\n"); // } // } //println 输出完会换行 //print 输出完不会换行 int i=1; while(i<=1000){ if(i%5==0){ System.out.print(i+"\t"); } if(i%(5*3)==0){ System.out.print("\n"); } i++; } } -
//练习3 九九乘法表 public static void main(String[] args) { //九行 第一个数 每行的开头都是行数 例如:3行 3*1 3*2 3*3 //第二个数 1到行数 例如第三行 1 2 3 for(int i=1;i<=9;i++){ for(int j=1;j<=i;j++){ System.out.print(i+"*"+j+"="+(i*j)+"\t"); } System.out.println(); } }
增强for循环
public static void main(String[] args) {
int[] numbers={10,20,30,40,50};//定义一个数组
//遍历数组的元素
for(int x:numbers){
System.out.println(x);
}
}
break和continue
-
break和continue都是用来控制循环结构的,主要作用是停止循环。
-
break用于跳出一个循环体或者完全结束一个循环,不仅可以结束其所在的循环,还可结束其外层循环。
-
只能在循环体内和switch语句体内使用break。
-
不管是哪种循环,一旦在循环体中遇到break,系统将完全结束循环,开始执行循环之后的代码。
-
当break出现在循环体中的switch语句体内时,起作用只是跳出该switch语句体,并不能终止循环体的执行。若想强行终止循环体的执行,可以在循环体中,但并不在switch语句中设置
-
continue语句的作用是跳过本次循环体中剩下尚未执行的语句,立即进行下一次的循环条件判定,可以理解为只是中止(跳过)本次循环,接着开始下一次循环。
-
continue语句并没有使整个循环终止。
-
continue 只能在循环语句中使用,即只能在 for、while 和 do…while 语句中使用。
-
goto关键字,"标签"是指后面跟一个冒号的标识符,例如:label:
public static void main(String[] args) { //打印101-150之间所有的质数 //质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数0 int count=0; //不建议使用 outer:for(int i=101;i<=150;i++){ for(int j=2;j<=i/2;j++){ if(i%j==0){ continue outer; } } System.out.println(i); } }
练习打印三角形
//打印三角形
public static void main(String[] args) {
//5行
for (int i = 1; i <=5; i++) {
for (int j = 5; j >=i; j--) {
System.out.print(" ");
}
for (int j = 1; j <=i ; j++) {
System.out.print("*");
}
for (int j = 1; j <i; j++) {
System.out.print("*");
}
System.out.println();
}
}
方法
-
return终止方法
-
public static void main(String[] args) { int max=max(10,10); System.out.println(max); } //比大小 public static int max(int num1,int num2){ int result=0; if(num1==num2){ System.out.println("num1==num2"); return 0;//return终止方法 } if(num1>num2){ result=num1; }else{ result=num2; } return result; } -
值传递(pass by value):在调用函数时,将实际参数复制一份传递到函数中,这样在函数中对参数进行修改,就不会影响到原来的实际参数;
引用传递(pass by reference):在调用函数时,将实际参数的地址直接传递到函数中。这样在函数中对参数进行的修改,就会影响到实际参数;
方法的重载
-
在同一个类中,方法名必须相同,参数列表不同(个数不同,类型不同,参数排列顺序不同等)。
-
方法的返回值可以相同也可以不相同。
-
仅仅返回类型不同不足以成为方法的重载。
命令行传参
public static void main(String[] args) {
for (int i = 0; i <args.length; i++) {
System.out.println("args["+i+"]:"+args[i]);
}
}
找到.java文件的文件夹进入cmd,javac Demo01.java
要退到src目录下 java com.gx.method.Demo01 this is gx
可变参数
-
在方法声明中,在指定参数类型后加一个省略号(...)。
-
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通参数必须在它之前声明。
-
public static void main(String[] args) { printMax(0,34,3,3,2,56.5); printMax(0,new double[]{1,2,3}); } public static void printMax(int x,double...numbers){ if(numbers.length==0){ System.out.println("No argument passed"); return;//结束方法 } double max=numbers[0]; //最大值 for(int i=1;i<numbers.length;i++){ if(numbers[i]>max){ max=numbers[i]; } } System.out.println(x); System.out.println("The max value is:"+max); }
递归
-
理解:A方法调用A方法,就是自己调用自己。
-
包括两个部分:递归头:什么时候不调用自身方法。如果没有头,将会陷入死循环。
递归体:什么时候需要调用自身方法。
-
//2! 2*1 //3! 3*2*1 public static void main(String[] args) { System.out.println(f(3));//6 } //2 2*f(1) 2*1 //3 3*f(2) 3*2*f(1) 3*2*1 public static int f(int n){ if(n==1){ return 1; }else{ return n*f(n-1); } } -
如果调用次数过多会影响机器性能
控制台计算器
//写一个计算器,实现加减乘除功能,并且能够循环的接收新的数据,通过用户交互实现。
public static void main(String[] args) {
while (true){
Scanner scanner=new Scanner(System.in);
System.out.println("请输入运算符:");
while (scanner.hasNext()){
String c=scanner.next();
System.out.println("请输入两个数a,b:");
while (scanner.hasNextDouble()){
double a=scanner.nextDouble();
double b=scanner.nextDouble();
switch (c){
case "+":
System.out.println("a+b为:"+add(a,b));
System.out.println("请输入运算符:");
break;
case "-":
System.out.println("a-b为:"+sub(a,b));
System.out.println("请输入运算符:");
break;
case "*":
System.out.println("a*b为:"+mul(a,b));
System.out.println("请输入运算符:");
break;
case "/":
System.out.println("a/b为:"+div(a,b));
System.out.println("请输入运算符:");
break;
default:
System.out.println("运算符或数字错误!");
System.out.println("请输入运算符:");
}
}
}
scanner.close();
}
}
public static double add(double a,double b){
return a+b;
}
public static double sub(double a,double b){
return a-b;
}
public static double mul(double a,double b){
return a*b;
}
public static double div(double a,double b){
return a/b;
}
数组
-
数组是相同类型数据的有序集合。
-
其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。如果越界,则报:ArrayIndexOutofBounds
-
数组也是对象。数组元素相当与对象的成员变量。
-
//变量的类型 变量的名字=变量的值; //数组类型 public static void main(String[] args) { int[]nums;//1.声明一个数组 int num2[]; nums=new int[2];//创建一个数组 //给数组元素中赋值 nums[0]=1; nums[1]=2; System.out.println(nums[0]); //计算所有元素的和 int sum=0; for (int i = 0; i <nums.length ; i++) { sum+=nums[i]; } System.out.println("总和为:"+sum); }
内存分析
-
堆存放new的对象和数组,可以被所有的线程共享,不会存放别的对象引用。
-
栈存放基本变量类型(会包含这个基本类型的具体数值),
存放引用对象的变量(会存放这个引用对象在堆里面的具体地址)。
-
方法区可以被所有的线程共享,包含了所有class和static变量。
-
![]()
数组的三种初始化
public static void main(String[] args) {
//静态初始化:创建+赋值
int[]a={1,2,3,4,5};
System.out.println(a[0]);//1
//动态初始化:包含默认初始化
int[]b=new int[10];
b[0]=10;
System.out.println(b[0]);//10
System.out.println(b[1]);//0 默认初始化
System.out.println(b[2]);//0 默认初始化
}
数组的使用
public static void main(String[] args) {
int[] arrays={1,2,3,4,5};
for (int array:arrays){
System.out.println(array);//12345
}
printArray(arrays);//12345
int[]reverse=reverse(arrays);
printArray(reverse);//54321
}
//打印数组元素
public static void printArray(int[] arrays){
for (int i = 0; i <arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
}
//反转数组
public static int[] reverse(int[] arrays){
int[] result=new int[arrays.length];
for (int i =0,j=arrays.length-1;i<arrays.length&&j>=0;i++,j--) {
result[j]=arrays[i];
}
return result;
}
多维数组
public static void main(String[] args) {
//[4][2]
/*
1,2 array[0]
2,3 array[1]
3,4 array[2]
4,5 array[3]
*/
int[][] array={{1,2},{2,3},{3,4},{4,5}};
System.out.println(array[0]);//打印堆中的地址
printArray(array[0]);//1 2
System.out.println();
System.out.println(array[0][0]);//1
System.out.println(array[0][1]);//2
System.out.println(array.length);//4
System.out.println(array[0].length);//2
for (int i = 0; i <array.length; i++) {
for (int j = 0; j <array[i].length ; j++) {
System.out.print(array[i][j]+" ");//1 2 2 3 3 4 4 5
}
}
}
//打印数组元素
public static void printArray(int[] arrays){
for (int i = 0; i <arrays.length; i++) {
System.out.print(arrays[i]+" ");
}
}
Arrays类
public static void main(String[] args) {
int[]a={1,2,3,4,9090,32,21,6,23};
System.out.println(a);
//打印数组元素
System.out.println(Arrays.toString(a));
printArray(a);
Arrays.sort(a);//数组进行排序:升序
System.out.println();
System.out.println(Arrays.toString(a));//[1, 2, 3, 4, 6, 21, 23, 32, 9090]
Arrays.fill(a,2,4,0);
System.out.println(Arrays.toString(a));//[1, 2, 0, 0, 6, 21, 23, 32, 9090]
}
public static void printArray(int[] a){
for (int i = 0; i <a.length; i++) {
if(i==0){
System.out.print("[");
}
if(i==a.length-1){
System.out.print(a[i]+"]");
}else{
System.out.print(a[i]+", ");
}
}
}
冒泡排序
-
时间复杂度O{n^2}
public static void main(String[] args) {
int[]a={1,3,5,23,2,7,6};
int[]sort=sort(a);
System.out.println(Arrays.toString(sort));
}
//冒泡排序
//1.比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置
//2.每一次比较,都会产生出一个最大或者最小的数字
//3.下一轮则可以少一次排序
//4.依次循环,知道结束
public static int[] sort(int[]array){
//外层循环,判断我们这个冒泡排序要走多少次成序
for (int i = 0; i <array.length-1 ; i++) {
boolean flag=false;//通过flag标识位减少没有意义的比较。
//内层循环,每次找到最大的,比较的次数少i次
for (int j = 0; j <array.length-1-i ; j++) {
//比较两个数,如果第一个数比第二个数大,则交换位置。
if(array[j]>array[j+1]){
int temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
flag=true;
}
}
if(flag==false){
break;
}
}
return array;
}
稀疏数组
-
当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。
-
public static void main(String[] args) { //1.创建一个二维数组 11*11 0:没有棋子 1:黑棋 2:白棋 int[][] array1=new int[11][11]; array1[1][2]=1; array1[2][3]=2; //输出原始的数组 System.out.println("输出原始的数组"); for(int[]ints:array1){ for (int anInt:ints){ System.out.print(anInt+"\t"); } System.out.println(); } System.out.println("===================="); //转换成稀疏数组保存 //获取有效值的个数 int sum=0; for (int i = 0; i <11 ; i++) { for (int j = 0; j <11 ; j++) { if(array1[i][j]!=0){ sum++; } } } System.out.println("有效值的个数为:"+sum); //2.创建一个稀疏数组的数组 int[][] array2=new int[sum+1][3]; array2[0][0]=11; array2[0][1]=11; array2[0][2]=sum; //遍历二维数组,将非零的值,存放到稀疏数组的数组中 int count=0; for (int i = 0; i <array1.length; i++) { for (int j = 0; j <array1[i].length ; j++) { if(array1[i][j]!=0){ count++; array2[count][0]=i; array2[count][1]=j; array2[count][2]=array1[i][j]; } } } //输出稀疏数组 System.out.println("输出稀疏数组"); for (int i = 0; i <array2.length; i++) { for (int j = 0; j <array2[i].length; j++) { System.out.print(array2[i][j]+"\t"); } System.out.println(); } System.out.println("==============================="); System.out.println("还原"); //1.读取稀疏数组 int[][] array3=new int[array2[0][0]][array2[0][1]]; //2.给稀疏数组的元素还原 for (int i = 1; i <array2.length; i++) { array3[array2[i][0]][array2[i][1]]=array2[i][2]; } //3.打印 System.out.println("输出还原的数组"); for(int[] ints:array3){ for(int anInt:ints){ System.out.print(anInt+"\t"); } System.out.println(); } }
面向对象(oop)
-
面向对象的本质就是:以类的方式组织代码,以对象的组织封装数据。
-
break:跳出switch,结束循环
return:方法结束
-
//值传递 public static void main(String[] args) { int a=1; System.out.println(a);//1 Demo04.change(a); System.out.println(a);//1 } //返回值为空 public static void change(int a){ a=10; } -
//引用传递:对象,本质还是值传递 public class Demo05 { public static void main(String[] args) { Person person=new Person(); System.out.println(person.name);//null Demo05.change(person); System.out.println(person.name);//高翔 } public static void change(Person person){ //person是一个对象:指向的---->Person person=new Person();这是一个具体的人,可以改变属性! person.name="高翔"; } } //定义了一个Person类,有一个属性:name class Person{ String name; }
构造器(构造方法)
-
和类名相同的方法,没有返回值。
-
一个类即使什么都不写,它也会存在一个构造方法(构造器),构造器用来初始化值。
-
使用new关键字,本质是在调用构造器。
-
有参构造:一旦定义了有参构造,无参就必须显示定义。
-
默认初始化: 数字:0 0.0 char:u0000 boolean:false 引用:null
-
public class Person { //一个类即使什么都不写,它也会存在一个构造方法(构造器) //显示的定义构造器 String name; //实例化 //1.使用new关键字,本质是在调用构造器 //2.用来初始化值 public Person(){ } //有参构造:一旦定义了有参构造,无参就必须显示定义 public Person(String name){ this.name=name; } //alt+insert 生成构造器 }
封装
1.提高程序的安全性,保护数据 2.隐藏代码的实现细节 3.统一接口 4.系统可维护性增加了 5.属性私有,使用get和set方法
继承
-
一个子类只能继承一个父类,子类可以继承父类的public的方法,private修饰父类的属性,子类通过get,set方法操作。
-
在java中,所有的类,都默认直接或者间接继承Object。
-
子类的无参构造方法会默认调用父类的无参构造方法,调用父类的构造器,必须在子类的构造器的第一行。
super
super注意点:
1.super调用父类的构造方法,必须在构造方法的第一个
2.super必须只能出现在子类的方法或者构造方法中
3.super和this不能同时调用构造方法
和this的比较:
代表的对象不同:
this:本身调用这个对象
super:代表父类对象的应用
前提:
this:没有继承也可以使用
super:只能在继承条件下才能使用
构造方法
this();本类的构造
super();父类的构造
重写
重写:需要有继承关系,子类重写父类的方法!
1.方法名必须相同
2.参数列表必须相同
3.修饰符:范围可以扩大 private<default<protected<public
4.抛出的异常:范围,可以被缩小,但不能扩大; ClassNotFoundException-->Exception(大)
重写,子类的方法和父类必须要一致,方法体不同!
为什么需要重写:
1.父类的功能,子类不一定需要,或者不一定满足!
2.Alt+Insert;override;
多态
-
即同一个方法根据发送对象的不同而采用多种不同的行为方式。
-
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多,父类的引用指向子类的对象,Person person=new Student();
-
对象能执行哪些方法,主要看对象左边的类型,和右边关系不大。
-
多态注意事项: 1.多态是方法的多态,属性没有多态 2.父类和子类,有联系,否则类型转换异常(ClassCastException) 3.存在条件:继承关系,方法需要重写,父类引用指向子类对象。 Father f1=new Son(); 1.static方法属于类,不属于实例 2.final修饰的是常量,方法无法重写,参数无法改变,存在常量池 3.private方法不能重写
instanceof和引用类型转换
对象运算符(instanceof)用来判断一个对象是否属于某个指定的类或其子类的实例,如果是,返回真(true),否则返回假(false)。
-
//Object>String //Objenct>Person>Teacher //Object>Person>Student Object object=new Student(); System.out.println(object instanceof Student);//true System.out.println(object instanceof Person);//true System.out.println(object instanceof Object);//true System.out.println(object instanceof Teacher);//false System.out.println(object instanceof String);//false System.out.println("=================================="); Person person=new Student(); System.out.println(person instanceof Student);//true System.out.println(person instanceof Person);//true System.out.println(person instanceof Object);//true System.out.println(person instanceof Teacher);//false //System.out.println(person instanceof String);//编译报错 System.out.println("=================================="); Student student=new Student(); System.out.println(student instanceof Student);//true System.out.println(student instanceof Person);//true System.out.println(student instanceof Object);//true //System.out.println(student instanceof Teacher);//编译报错 //.out.println(student instanceof String);//编译报错 -
public static void main(String[] args) { //引用类型之间的转化:父 子 //高到低,父类转子类 Person obj=new Student(); //obj将这个对象转换为student类型,我们就可以使用studnet类型的方法了 Student student=(Student)obj; student.go(); //低到高,子类转父类,可能丢失自己的本来一些方法 Student student1=new Student(); student1.go(); Person person=student; ((Student) person).go(); } -
/* 1.父类引用指向子类对象 2.把子类转换成父类,向上转型,可能会丢失自己的本来一些方法。 3.把父类转换成子类,向下转型,强制转换 4.方便方法的调用,减少重复代码!简介 */
static
-
//static public class Student { private static int age;//静态的变量 private double score;//非静态的变量 public void run(){ go(); } public static void go(){ //run();错误 } public static void main(String[] args) { Student s1 = new Student(); // System.out.println(Student.score);错误 System.out.println(Student.age);//0 System.out.println(s1.age);//0 System.out.println(s1.score);//0.0 go(); } } -
//代码块 public class Person { //2:赋初值 { //代码块(匿名代码块) System.out.println("匿名代码块"); } //1:只执行一次 static { //静态代码块 System.out.println("静态代码块"); } //3 public Person(){ System.out.println("构造方法"); } public static void main(String[] args) { Person person = new Person(); System.out.println("==============="); Person person1 = new Person(); /* 静态代码块 匿名代码块 构造方法 =============== 匿名代码块 构造方法 */ } }
抽象类
-
abstract修饰的类
-
抽象方法必须在抽象类中,抽象方法是abstract修饰的方法,只有方法名字,没有方法实现
-
抽象类的所有方法,继承了它的子类,都必须要实现它的方法
-
抽象类中可以写普通方法
-
不能new这个抽象类,只能靠子类去实现它:约束!
接口
-
interface声明的类,接口需要有实现类。
-
接口中的所有定义都是抽象的public,public abstract void run(); 默认简化void run();
-
接口不能实例化,接口中没有构造方法。
-
implements可以实现多个接口。
-
必须要重写接口中的方法。
内部类
-
就是在一个类的内部在定义一个类。
-
内部类可以直接方法外部类方法和属性,不需要创建外部类的对象。
-
一个java文件可以有多个class类,但是只能有一个public class
成员内部类
public class Outer {
private int id=10;
public void out(){
System.out.println("这是外部类的方法");
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//获得外部类的私有属性,私有方法
public void getID(){
out();
System.out.println(id);
}
}
}
静态内部类
public class Outer {
private int id=10;
public void out(){
System.out.println("这是外部类的方法");
}
//静态内部类无法直接访问外部类的属性方法了
public static class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
}
}
局部内部类
public class Outer {
//局部内部类
public void method(){
class Inner{
public void in(){
System.out.println("局部内部类的方法");
}
}
}
}
匿名内部类
public class Test {
public static void main(String[] args) {
//没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
new UserService(){
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("1");
}
}
interface UserService{
void hello();
}
异常
-
Throwable:
-
Exception:运行时异常:1. 1/0 2.ClassNotFound 3.NullPoint 4.UnkownType 5.ArrayIndexOutofBounds
检查型异常
-
Error:(前端布局错误)AWT错误
JVM错误 : 1.StackOverFlow 2.OutofMemory
-
-
Error和Exception的区别:Error通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常时,java虚拟机(JVM)一般会终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。
-
public class Test { public static void main(String[] args) { int a=1; int b=0; //假设要捕获多个异常:要从小到大 try{//try监控区域 System.out.println(a/b); }catch (Error e){//catch(想要捕获的异常类型) System.out.println("Error"); }catch(Exception e){ System.out.println("Exception"); }catch (Throwable t){ System.out.println("Throwable"); } finally {//处理善后工作,finally可以不要,假设IO,资源关闭。 System.out.println("finally"); } } } -
public static void main(String[] args) { try { new Test2().test(1,0); } catch (ArithmeticException e) { e.printStackTrace(); } } //假设这方法中,处理不了这个异常。方法上抛出异常 public void test(int a,int b)throws ArithmeticException{ if (b==0){// throw throws throw new ArithmeticException();//主动抛出异常,一般在方法里使用 } System.out.println(a/b); }
自定义异常
//自定义异常
public class MyException extends Exception {
//传递数字>10;
private int detail;
public MyException(int detail) {
this.detail = detail;
}
//toString:异常的打印信息
@Override
public String toString() {
return "MyException{" +
"detail=" + detail +
'}';
}
}
public class Test {
//可能会存在异常的方法
static void test(int a) throws MyException {
System.out.println("传递的参数为:"+a);
if(a>10){
throw new MyException(a);//抛出
}
System.out.println("OK");
}
public static void main(String[] args){
try {
test(11);
} catch (MyException e) {
System.out.println("MyException=>"+e);
}
}
}
Java API(Application Programming Interface)
-
中文名:应用程序编程接口,通常叫“Java文档”,是Java中提供的类的使用说明书,是程序员和Java语言之间沟通的桥梁。
-
学习API其实就是学习如何使用JDK提供的类。
-
Java API可以在
Object(java.lang)
-
类层次结构最顶层的基类,所有类都直接或间接的继承自Object类,所以,所有的类都是一个Object(对象)。
-
所有类由Object():构造一个对象,子类对象初始化时都会优先调用该方法 。
-
int hashCode();返回对象的哈希码值,该方法通过对象的地址值进行计算,不同对象的返回值一般不同。
-
class<?>getClass();返回调用此方法对象的运行时类对象(调用者的字节码文件对象)。
-
String toString();返回该对象的字符串表示。
-
boolean equals();返回其它某个对象是否与此对象“相等”。默认情况下比较两个对象的引用(地址值),建议重写 。
-
//重写toString()和equals() public class Student { private int id; private String name; private double score; public Student(int id, String name, double score) { this.id = id; this.name = name; this.score = score; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", score=" + score + '}'; } @Override public boolean equals(Object obj) { //s1.equals(s2); //this: s1 o: s2 //比较两个对象的地址值是否相同, 提高效率. //补充: 如果if语句控制的语句体只有一行代码,那么{}可以省略不写. if (this == obj) return true; //判断要比较的两个对象是否是同一个类型的对象, 提高程序的健壮性. if (obj == null || getClass() != obj.getClass()) return false; //向下转型, 正常的逻辑代码. Student student = (Student) obj; return id == student.id && score == student.score && name.equals(student.name); } } public class Test { public static void main(String[] args) { //测试toString()方法. //创建学生类的对象. Student s1 = new Student(1, "欧阳修", 66); //输出语句直接打印对象, 默认调用了该对象的toString()方法. System.out.println(s1); System.out.println(s1.toString()); //测试equals()方法, s1.equals(s2); Student s2 = new Student(1, "欧阳修", 66); boolean b1 = s1.equals(s2); System.out.println(b1); } }
Scanner类(java.util)
-
Scanner(InputSteam);构造一个扫描器对象,从指定输入流中获取数据参数System.in,对应键盘录入。
-
hasNextXxx();判断是否还有下一个输入项,其中Xxx可能是任意基本数据类型,返回结果为布尔类型。
-
nextXxx();获取下一个输入项,其中Xxx可能是任意基本数据类型,返回对应类型的数据。
-
String nextLine();获取下一行数据。以换行符作为分隔符。
-
String next();获取下一个输入项,以空白字符作为分隔符 空白字符:空格、tab、回车等。
-
public class Test { public static void main(String[] args) { //创建Scanner类型的对象(注意: 要导包) //System.in: 标准的输入流, 默认指向键盘. Scanner sc = new Scanner(System.in); //接收整数. /*System.out.println("请录入一个整数: "); //为了解决(避免)InputMismatchException异常, 可以加入一个判断 if (sc.hasNextInt()) { //判断下一个录入的是否是整数, 如果是, 结果就是true int num = sc.nextInt(); System.out.println("num: " + num); }*/ //接收字符串类型的数据 System.out.println("请录入一个字符串:"); /*String str1 = sc.nextLine(); //结束标记是: 换行符 System.out.println("str1: " + str1);*/ String str2 = sc.next(); //结束标记: 空白字符(空格, tab, 换行符) System.out.println("str2: " + str2); sc.close(); } }
String类(String)
-
int length();获取当前字符串的长度。
-
char charAt(int index);获取指定索引位置的字符。
-
int indexOf(String);获取指定字符(串)第一次出现的索引。
-
int lastIndexOf(String);获取指定字符(串)最后一次次出现的索引。
-
String substring(int);获取指定索引位置(含)之后的字符串。
-
String substring(int,int);获取从索引start位置(含)起至索引end位置(不含)的字符串。
-
public class Test { public static void main(String[] args) { //定义一个字符串 String str = "java 回车课堂 java"; //int length(): 获取当前字符串的长度 /*int length = str.length(); System.out.println(length);*/ System.out.println(str.length()); //char charAt(int index): 获取指定索引位置的字符 char ch = str.charAt(1); System.out.println(ch); //int indexOf(String): 获取指定字符(串)第一次出现的索引 //需求: 字符'a'第一次出现的位置 int index1 = str.indexOf('a'); System.out.println("index1: " + index1); //int lastIndexOf(String): 获取指定字符(串)最后一次次出现的索引 //需求: 字符'a'最后一次出现的位置 int index2 = str.lastIndexOf('a'); System.out.println("index2: " + index2); //String substring(int): 获取指定索引位置(含)之后的字符串 //需求: 截取从索引5开始的所有元素 String s1 = str.substring(5); System.out.println("s1: " + s1); //String substring(int, int): 获取从索引start位置(含)起至索引end位置(不含)的字符串 //需求: 截取索引5, 10的所有元素 String s2 = str.substring(5,10); System.out.println("s2: " + s2); //? } } -
byte[] getBytes();将字符串转换成字节数组
-
char[] toCharArray(); 将字符串转换成字符数组。
-
static String valueOf();将指定类型数据转换成字符串。
-
String replace(old,new);将指定字符(串)替换成新的字符(串)。
-
String[] split(String);切割字符串,返回切割后的字符串数据,原字符串不变。
-
String trim();去掉字符串两端的空白字符。
-
public class Test01 { public static void main(String[] args) { //定义一个字符串 String s1 = "abc"; //byte[] getBytes(): 将字符串转换成字节数组 byte[] bys = s1.getBytes(); //97, 98, 99 for (int i = 0; i < bys.length; i++) { System.out.println(bys[i]); } System.out.println("---------------------"); //char[] toCharArray(): 将字符串转换成字符数组 char[] chs = s1.toCharArray(); //'a','b','c' for (int i = 0; i < chs.length; i++) { System.out.println(chs[i]); } System.out.println("---------------------"); //static String valueOf(..) : 将指定类型数据转换成字符串 //整数123 --> 字符串"123" String s2 = String.valueOf(123); System.out.println(s2 + 4); //在实际开发中, 上述的方式基本上都会用下边的这行代码替代 String s3 = "" + 123; System.out.println(s3 + 4); System.out.println("---------------------"); //String replace(old, new): 将指定字符(串)替换成新的字符(串) String s4 = "abc abc abc"; //'d' 替换 'b' String s5 = s4.replace('b','d'); System.out.println("s5: " + s5); System.out.println("---------------------"); //String[] split(String): 切割字符串,返回切割后的字符串数据,原字符串不变 //将字符串s4, 按照空格进行切割 //"abc" "abc" "abc" String[] arr = s4.split(" "); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } System.out.println("---------------------"); //String trim(): 去掉字符串两端的空白字符 String s6 = " a b c "; String s7 = s6.trim(); System.out.println("s6: " + s6); System.out.println("s7:" + s7); } }
StringBuilder和StringBuffer(java.lang)
-
StringBulider();构造一个空的StringBuilder容器。
-
StringBulider(String);构造一个StringBuilder容器,并添加指定字符串。
-
StringBulider append(...);将任意数据添加到StringBuilder容器中。
-
String toString();将当前StringBuilder容器转成字符串。
-
public class Test { public static void main(String[] args) { //测试构造方法 //测试空参构造 StringBuilder sb = new StringBuilder(); StringBuilder sb2 = sb.append("abc"); System.out.println("sb: " + sb); System.out.println("sb2: " + sb2); System.out.println("----------------"); //需求: 将String类型的"abc" 转成 StringBuilder类型的对象 StringBuilder sb3 = new StringBuilder("abc"); System.out.println("sb3: "+ sb3); System.out.println("----------------"); //测试成员方法 //需求: 将三个字符串拼接成一个新的字符串: 学Java, 到传智播客 找小黑! StringBuilder sb4 = new StringBuilder(); sb4.append("学Java,"); sb4.append("到传智播客"); sb4.append("找小黑!"); System.out.println("sb4: " + sb4); System.out.println("----------------"); String s = sb4.toString(); System.out.println("字符串s: " + s); } }
6.String和StringBulider以及StringBuffer的区别:
-
String不可变字符,StringBuilder可变字符、效率高、线程不安全,StringBuffer可变字符、效率低、线程安全。
Data和Calendar(java.util)
-
Date();构造一个日期对象,当前系统时间,精确到毫秒。
-
Date(long)l;构造一个日期对象,时间为自“1970年1月1日00:00:00 GMT”起,至指定参数的毫秒数。
-
long getTime();**将日期对象转换成对应时间的毫秒值 。
-
public class Test { public static void main(String[] args) { //测试Date类 //测试空参构造, 采用当前操作系统的默认时间 Date date1 = new Date(); System.out.println("date1:" + date1); //获取当前操作系统时间的毫秒值 long time = date1.getTime(); System.out.println("time:" + time); //Sun Jun 06 17:04:39 CST 2066 --> 3043040679456 //创建一个指定的时间 Date date2 = new Date(3043040679456L); System.out.println("date2:" + date2); } } -
static Calendar getInstance();根据当前系统时区和语言环境获取日历对象。
-
int get(int field);返回给定日历字段的值。
-
void set(int field,int value);将给定的日历字段设置为指定的值。
-
public class Test01 { public static void main(String[] args) { //创建Calendar类型的对象. Calendar c = Calendar.getInstance(); System.out.println(c); //获取年月日的信息 int year = c.get(Calendar.YEAR); int month = c.get(Calendar.MONTH); //Java中使用0-11的数字表示月份的, 对应1-12月 int day = c.get(Calendar.DATE); System.out.println(year + "年" + (month + 1) + "月" + day + "日"); System.out.println("------------------------"); //设置指定时间为: 2022年2月2日 /* c.set(Calendar.YEAR,2022); int year2 = c.get(Calendar.YEAR); System.out.println(year2 + "年" + (month + 1) + "月" + day + "日");*/ c.set(2022, 1, 2); //重新获取年月日的值 int year2 = c.get(Calendar.YEAR); int month2 = c.get(Calendar.MONTH); //Java中使用0-11的数字表示月份的, 对应1-12月 int day2 = c.get(Calendar.DATE); System.out.println(year2 + "年" + (month2 + 1) + "月" + day2 + "日"); } }
基本类型的包装类
-
由于基本数据类型不是对象,所以不能直接调用方法,故Java针对基本类型提供了对应的包装类,以对象的形式来使用。
-
byte=>Byte;short=>Short;int=>Integet;long=>Long;char=>Character;float=>Float;double=>Double;boolean=>Boolean;
-
装箱:基本类型转包装类型(对象类型)。
-
拆箱:包装类型(对象类型转基本类型)。
-
基本类型parseXxx(String);将字符串类型的数据转换成对应的基本类型。例如:static int parseInt(String);
-
除了Character类以外, 其他的7种包装类都有parseXXX()方法. 因为如果字符串想转换成char类型的数据, 可以通过: String类中的方法toCharArray(), charAt();
-
public class Test { public static void main(String[] args) { //因为变量a属于基本类型, 不能通过 对象名. 的形式调用方法. //解决方案: 将其转换成对应的 包装类(引用类型)即可. int a = 10; //装箱 Integer i1 = new Integer(20); //拆箱 int b = i1.intValue(); System.out.println(i1); System.out.println(b); System.out.println("------------------"); //JDK5的新特性, 自动拆装箱. Integer i2 = 30; //装箱 int c = i2; //拆箱 System.out.println("---------------------------"); //需求: 将字符串类型的"10", 转换成int类型的 10 String s = "10"; int num = Integer.parseInt(s); System.out.println("num: " + num); System.out.println("num + 100 = " + (num + 100)); } }
IO流
-
I/O,即输入(Input)输出(Output),IO流指的是数据像连绵的流体一样进行传输,可以在本地磁盘或网络上传输(读/写)数据。
-
字符流:按字符读写数据的IO流
-
Reader: 字符输入流的顶层抽象类
-
FileReader: 普通的字符输入流.
-
BufferedReader: 高效的字符输入流(也叫: 字符缓冲输入流)
-
-
Writer:字符输出流的顶层抽象类
-
FileWriter: 普通的字符输出流
-
BufferedWriter: 高效的字符输出流(也叫: 字符缓冲输出流)
-
字节流: 以字节为单位来操作数据
-
InputStream: 字节输入流的顶层抽象类
-
FileInputStream: 普通的字节输入流
-
BufferedInputStream: 高效的字节输入流(也叫: 字节缓冲输入流)
-
-
OutputStream: 字节输出流的顶层抽象类
-
FileOutputStream: 普通的字节输出流.
-
BufferedOutputStream: 高效的字节输出流(也叫: 字节缓冲输出流)
-
-
File类
-
文件,文件夹,一个File对象代表磁盘上的某个文件或文件夹。
-
File(String pathname) File(String parent, String child) File(File parent, String child)
-
boolean createNemFile();创建文件。
-
boolean mkdir();和boolean mkdirs();创建目录。
-
isDirectory();判断File对象是否为目录。
-
isFile();判断File对象是否为文件。
-
exists();判断File对象是否存在。
-
getAbsoulutePath();获取绝对路径。绝对路径指的是从本地磁盘开始的路径。
-
getPath();获取文件的相对路径。相对路径指的是相对某一位置的路径,在Java项目中,相对路径从项目名开始。
-
getName();获取文件名。
-
list();获取指定目录下所有文件(夹)名称数组。
-
listFiles();获取指定目录下所有文件(夹)File数组。
-
将 C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt 封装成File对象 public class Test { public static void main(String[] args) throws IOException { //方式一: 根据字符串形式的路径获取File对象. File file1 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt"); System.out.println("file1: " + file1); //方式二: 根据字符串形式的父目录以及子目录创建File对象. File file2 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\", "1.txt"); System.out.println("file2: " + file2); //方式三: 根据父目录对象, 以及字符串形式的子目录来获取File对象. File file3 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\"); File file4 = new File(file3, "1.txt"); System.out.println("file4: " + file4); } } -
//在C:\\Users\\GX\\Desktop\\杂货\\File类学习\\下创建文件和文件夹 public class Test01 { public static void main(String[] args) throws IOException { File file5 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt"); boolean flag1 = file5.createNewFile(); System.out.println("flag1: " + flag1); //需求: 在D:盘下创建 a文件夹 File file6 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\a"); boolean flag2 = file6.mkdir(); //make directory, 创建单级目录 System.out.println("flag2: " + flag2); //需求: 在D:盘下创建 a/b/c文件夹 File file7 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\a\\b\\c"); boolean flag3 = file7.mkdirs(); //创建多级目录(也可以创建单级目录) System.out.println("flag3: " + flag3); } } -
//运用成员方法对文件/文件夹做判断 public class Test02 { public static void main(String[] args) throws IOException { File file8 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt"); /* boolean flag4 = file8.isDirectory(); System.out.println("测试file8是否是文件夹: " + flag4 );*/ System.out.println("测试file8是否是文件夹: " + file8.isDirectory() ); System.out.println("测试file8是否是文件: " + file8.isFile()); System.out.println("测试file8是否存在: " + file8.exists()); } } -
//获取绝对路径 //获取文件的相对路径 //获取文件名 //获取指定目录下所有文件(夹)名称数组 //获取指定目录下所有文件(夹)File数组 public class Test03 { public static void main(String[] args) { File file1 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt"); //获取file1的绝对路径 String path1 = file1.getAbsolutePath(); System.out.println("绝对路径: " + path1); //获取file1的相对路径 String path2 = file1.getPath(); System.out.println("相对路径: " + path2); //获取文件名 String fileName = file1.getName(); System.out.println("文件名: " + fileName); System.out.println("--------------------"); //获取lib文件夹下所有的文件(夹)的: 名称数组String[] File file2 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习"); String[] names = file2.list(); for (String name : names) { System.out.println(name); } System.out.println("--------------------"); //获取lib文件夹下所有的文件(夹)的: File对象数组 File[] File[] files = file2.listFiles(); for (File file : files) { System.out.println(file); } } }
字符流读写文件
//1.txt文件中存储着字符串“abc”,请通过字符流读取数据
public class ReaderDemo1 {
public static void main(String[] args) throws IOException {
//需求: 通过字符流读取数据.
//1. 创建字符输入流对象.
Reader reader = new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
//2. 读取数据.
//定义变量, 用来接收读取到的字符.
int ch;
while((ch = reader.read()) != -1){
//ch = reader.read();
System.out.println(ch);
}
//3. 释放资源.
reader.close();
}
}
//2.txt文件中存储着字符串“abcdef”,要求通过字符流读取数据, 一次读取一个字符数组
public class ReaderDemo2 {
public static void main(String[] args) throws IOException {
//需求: 通过字符流读取数据, 一次读取一个字符数组.
//1. 创建字符输入流对象.
Reader reader = new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt");
//2. 读取数据.
//定义字符数组
char[] chs = new char[3];
//定义一个变量, 记录读取到的有效字符数.
int len;
while((len = reader.read(chs)) != -1) {
//将读取到的内容, 转换成字符串, 然后打印.
String s = new String(chs,0,len); //[g,e,f], 0, 1 chs: 表示要操作的数组 0: 表示起始索引 len: 表示要操作的字符的个数
System.out.println(s);
}
//3. 释放资源.
reader.close();
}
}
//字符流写数据 – 按单个字符写入
public class WriteDemo {
public static void main(String[] args) throws IOException {
//1. 创建字符输出流对象.
Writer writer = new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
//2. 写数据.
//一次写一个字符
writer.write('好');
//3. 释放资源.
writer.close();
}
}
//字符流写数据 – 按字符数组/字符串写入
public class WriteDemo01 {
public static void main(String[] args) throws IOException {
//1. 创建字符输出流对象.
Writer writer = new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
//2. 写数据.
//一次写一个指定的字符数组
char[] chs = {'黑','马','程','序','员'};
writer.write(chs,2,3);
//一次写一个字符串
writer.write("好好学习, 知识改变命运!");
//3. 释放资源.
writer.close();
}
}
//通过字符流拷贝文件, 一次读写一个字符(将1.txt文件中的内容复制到2.txt文件中)
public class CopyFile {
public static void main(String[] args) throws IOException {
//1 创建字符输入流对象, 关联数据源文件
//Reader reader = new FileReader("lib/1.txt");
FileReader fr = new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
//2 创建字符输出流对象, 关联目的地文
FileWriter fw = new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt"); //细节: 如果目的地文件不存在, 程序会自动创建
//3 定义变量,记录读取到的内容
int len;
//4 循环读取,只要条件满足就一直读,并将读取到的内容赋值给变
while ((len = fr.read()) != -1) {
//5 将读取到的数据写入到 目的地文件中
fw.write(len);
}
//6 释放资源
fr.close();
fw.close();
}
}
//通过字符缓冲流, 将1.txt文件中的内容拷贝到2.txt文件中
public class CopyFile02 {
public static void main(String[] args) throws IOException {
//1. 创建字符缓冲输入流对象, 关联数据源文件.
//1.1 创建普通的字符输入流对象
FileReader fr = new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
//1.2 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(fr);
//简化上述的代码
//BufferedReader br2 = new BufferedReader(new FileReader("lib/1.txt"));
//2. 创建字符缓冲输出流对象, 关联目的地文件.
//2.1 创建普通的字符输出流对象.
FileWriter fw = new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt");
//2.2 创建字符缓冲输出流对象.
BufferedWriter bw = new BufferedWriter(fw);
//3. 定义变量, 记录读取到的数据.
int len;
//4. 循环读取, 只要条件满足就一直读, 并将读取到的内容赋值给变量.
while((len = br.read()) != -1) {
//5. 将读取到的数据写入到目的地文件中.
bw.write(len);
}
//6. 释放资源.
br.close();
bw.close();
}
}
//通过字符缓冲流"一次读写一行"的方式, 将1.txt文件中的内容拷贝到2.txt文件中
public class CopyFile03 {
public static void main(String[] args) throws IOException {
//1. 创建字符缓冲输入流对象, 关联数据源文件.
BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt"));
//2. 创建字符缓冲输出流对象, 关联目的地文件.
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt"));
//3. 定义变量, 记录读取到的内容.
String str;
//4. 循环读取, 只要条件满足就一直读, 并将读取到的内容赋值给变量.
while((str = br.readLine()) != null) {
//5. 将读取到的内容写入到目的地文件中.
bw.write(str);
//换行
bw.newLine();
}
//6. 释放资源.
br.close();
bw.close();
}
}
字符流只能拷贝纯文本文件。
字节流读写文件
//通过普通的字节流, 一次读写一个字节的方式将lib/1.jpg赋值到lib/2.jpg中
public class Steam01 {
public static void main(String[] args) throws IOException {
//1.创建字节输入流, 关联数据源文件
FileInputStream fis = new FileInputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.jpg");
//2.创建字节输出流, 关联目的地文件
FileOutputStream fos = new FileOutputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.jpg");
//3.创建字节输出流, 关联目的地文件
int len;
//4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
while((len = fis.read()) != -1) {
//5.将读取到的数据写入到目的地文件中
fos.write(len);
}
//6.释放资源
fis.close();
fos.close();
}
}
//通过普通字节流一次读写一个数组的方式将1.jpg复制到2.jpg中
public class Stream02 {
public static void main(String[] args) throws IOException {
//1.创建字节输入流对象, 关联数据源文件
FileInputStream fis = new FileInputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.jpg");
//2.创建字节输出流对象, 关联目的地文件
FileOutputStream fos = new FileOutputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.jpg");
//3.创建字节输出流, 关联目的地文件
byte[] bys = new byte[1024];
//用来记录读取到的有效字节数
int len;
int sum=0;
//4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
while((len = fis.read(bys)) != -1) {
//5.将读取到的数据写入到目的地文件中
fos.write(bys,0,len);
}
//6.释放资料
fis.close();
fos.close();
}
}
//通过字节缓冲流,将1.jpg文件中的内容拷贝到2.jpg文件中
public class Stream03 {
public static void main(String[] args) throws IOException {
//1. 创建字节缓冲输入流对象, 关联数据源文件.
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.jpg"));
//2. 创建字符缓冲输出流对象, 关联目的地文件.
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.jpg"));
//3. 定义变量, 记录读取到的数据.
int len;
//4. 循环读取, 只要条件满足就一直读, 并将读取到的内容赋值给变量.
while ((len = bis.read()) != -1) {
//5. 将读取到的数据写入到目的地文件中.
bos.write(len);
}
//6. 释放资源.
bis.close();
bos.close();
}
}
使用控制台模拟实际开发中上传用户头像的功能
//使用控制台模拟实际开发中上传用户头像的功能
//分析:
//
// 在控制台录入用户头像的路径
// 解析路径字符串中文件名是否合法:后缀名为:.jpg、.png、.bmp
// 判断该路径表示的File对象是否存在,是否为文件:
// file.exist()
// file.isFile()
// 读取该文件并写入到指定目录
// 提示头像上传成功 或 失败
// 实现步骤:
//
// 定义一个方法,用来获取要上传的用户头像路径. getPath(),返回文件对象
// 定义一个方法,用来判断要上传的用户头像,在lib文件夹中是否存在.isExists(String path),需要传入文件名
// 如果存在,提示:该用户头像已经存在,上传失败
// 如果不存在,就上传该用户头像,并提示上传成功,这里也需要定义一个头像上传函数
public class Total {
public static void main(String[] args) throws IOException {
//需求:模拟用户上传头像功能,假设所有的用户头像都应该上传到:项目下的目标文件夹中
//1. 定义一个方法,用来获取要上传的用户头像路径. getPath();
File path =getPath();
System.out.println(path);
//2. 定义一个方法,用来判断要上传的用户头像,在文件夹中是否存在
boolean flag = isExists(path.getName());
//3. 如果存在,提示:该用户头像已经存在,上传失败
if (flag) {
System.out.println("该用户头像已经存在,上传失败");
} else {
//4. 如果不存在,就上传该用户头像,并提示上传成功
//数据源文件 目的地文件
//d:\picture\1.png ——> lib/1.png
uploadFile(path);
}
}
public static File getPath() {
//1.1 提示用户录入要上传的用户头像路径,并接收
Scanner sc = new Scanner(System.in);
//1.2 因为不知道用户多少次能录入正确,所以用while(true)改进
while (true) {
System.out.println("请录入您要上传的用户头像路径");
String path = sc.nextLine();
//1.3 判断该路径的后缀名是否是:.jpg .png .bmp
//1.4 如果不是,就提示您录入的不是图片,请重新录入;
if (!path.endsWith(".jpg") && !path.endsWith(".png") && !path.endsWith("bmp")) {
System.out.println("您录入的不是图片,请重新录入:");
//细节,千万注意,别忘了写
continue;
}
//1.5 如果是,程序接着执行,并判断该路径是否存在,并且是否是文件
File file = new File(path);
if (file.exists() && file.isFile()) {
//1.6 如果是,说明就是我们想要的数据(图片,文件),直接返回
return file;
} else {
//1.7 如果不是,就提示:您录入的路径不合法,请重新录入;
System.out.println("您录入的路径不合法,请重新录入:");
}
}
}
public static boolean isExists(String path) {//1.png
//2.1 将lib文件夹封装成File对象
File file = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习");
//2.2 获取lib文件夹中所有文件(夹)的名称数组
String[] names = file.list();
//2.3 遍历第二步获取到的数组,用获取到的数据一次和path进行比较
for (String name : names) {
if (name.equals(path)) {
//2.4 如果一致,说明该用户头像已经存在了,就返回true
return true;
}
}
//2.5 如果不一致,说明该用户头像不存在,就返回false
return false;
}
public static void uploadFile(File path) throws IOException {
//4.1 创建字节输入流,关联数据源文件
//FileInputStream(File file)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path)); //paht: d:\picture\1.png
//4.2 创建字符输出流,关联目的地文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\" + path.getName())); // lib/1.png
//4.3 定义变量,记录读取到的数据
int len;
//4.4 循环读取,只要条件满足就一直读,并将读取到的数据赋值给变量
while ((len = bis.read()) != 1) {
//4.5 将读取到的数据写入到目的地文件夹
bos.write(len);
}
//4.6 释放资源
bis.close();
bos.close();
}
}
多线程
-
程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
-
进程:是程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。
-
一个进程可以包含多个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是cpu调度和执行的单位。
第一种方法继承Tread类,重写run();方法,子类对象.start(),不建议使用因为单继承局限性
-
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程 //总结:注意:线程开启不一定立即执行,由cpu调度执行 public class TestThread extends Thread{ @Override public void run() { //run方法线程体 for (int i = 0; i <20 ; i++) { System.out.println("我在看代码----"+i); } } public static void main(String[] args) { //main线程,主线程 //创建一个线程对象 TestThread testThread1=new TestThread(); //调用start()方法开启线程,同时执行 testThread1.start(); for (int i = 0; i <20 ; i++) { System.out.println("我在学习多线程--"+i); } } } -
//练习Thread,是实现多线程同步下载图片 public class TestThread01 extends Thread{ private String url;//网络图片地址 private String name;//保存的文件名 public TestThread01(String url,String name){ this.url=url; this.name=name; } @Override public void run() { WebDownloader webDownloader=new WebDownloader(); webDownloader.downloader(url,name); System.out.println("下载的文件名为:"+name); } public static void main(String[] args) { TestThread01 testThread01=new TestThread01("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果1"); TestThread01 testThread02=new TestThread01("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果2"); TestThread01 testThread03=new TestThread01("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果3"); testThread01.start();//线程不是顺序执行的 testThread02.start(); testThread03.start(); } } //下载器 class WebDownloader{ //下载方法 public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downloader方法出现问题"); } } }
第二种方法定义MyRunnable类实现Runnable接口,重写run();方法,编写线程执行体,创建线程对象,调用start();方法启动线程,推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用。
//创建线程方法2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start();方法
public class TestThread02 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i <20 ; i++) {
System.out.println("我在看代码----"+i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
TestThread02 testThread02=new TestThread02();
//创建线程对象,通过线程对象来开启我们的线程,代理
Thread thread=new Thread(testThread02);
//调用start()方法开启线程,同时执行
thread.start();
for (int i = 0; i <20 ; i++) {
System.out.println("我在学习多线程--"+i);
}
}
}
//多个线程同时操作同一个对象
//买火车票的例子
//发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
public class TestThread03 implements Runnable{
//票数
private int ticketNums=10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
try {
//模拟延时
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->拿到了"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestThread03 testThread03=new TestThread03();
new Thread(testThread03,"小明").start();
new Thread(testThread03,"老师").start();
new Thread(testThread03,"黄牛").start();
}
}
//模拟龟兔赛跑
public class Race implements Runnable {
private static String winner;
@Override
public void run() {
for (int i = 0; i <=100; i++) {
//模拟兔子休息
if(Thread.currentThread().getName().equals("兔子")&&i%10==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag=gameOver(i);
//如果比赛结束了,就停止程序
if(flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
}
}
//判断是否完成比赛
private boolean gameOver(int steps){
//判断是否有胜利者
if(winner!=null){
//已经存在胜利者了
return true;
}else{
if(steps>=100){
winner=Thread.currentThread().getName();
System.out.println("winner is"+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race=new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
实现Callable接口
//线程创建方式三:实现callable接口
/*
callable的好处
1.可以定义返回值
2.可以抛出异常
*/
public class TestCallable implements Callable {
private String url;//网络图片地址
private String name;//保存的文件名
public TestCallable(String url, String name){
this.url=url;
this.name=name;
}
@Override
public Boolean call() {
WebDownloader webDownloader=new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载的文件名为:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1=new TestCallable("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果1");
TestCallable t2=new TestCallable("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果2");
TestCallable t3=new TestCallable("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果3");
//创建执行服务
ExecutorService ser= Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1=ser.submit(t1);
Future<Boolean> r2=ser.submit(t2);
Future<Boolean> r3=ser.submit(t3);
//获取结果
boolean rs1=r1.get();
boolean rs2=r2.get();
boolean rs3=r3.get();
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
//关闭服务
ser.shutdownNow();
}
}
FutureTask<Integer> futureTask=new FutureTask<Integer>(new MyThread3()); new Thread(futureTask).start();
Lamda表达式
-
定义一个函数式接口(只有一个抽象方法的接口)。
-
避免匿名内部类定义过多,让代码更加简洁。
-
lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹。 前提是函数式接口 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
-
/* 推导Lambda表达式 */ public class TestLambda { //3.静态内部类 static class Like2 implements ILike { @Override public void Lambda() { System.out.println("i like lambda"); } public static void main(String[] args) { ILike iLike = new Like(); iLike.Lambda(); iLike = new Like2(); iLike.Lambda(); //4.局部内部类 class Like3 implements ILike { @Override public void Lambda() { System.out.println("i like lambda"); } } iLike=new Like3(); iLike.Lambda(); //5.匿名内部类,没有类的名称,必须借助接口或者父类 iLike=new ILike() { @Override public void Lambda() { System.out.println("i like lambda"); } }; iLike.Lambda(); //6.用Lambda简化 iLike=()->{ System.out.println("i like lambda");}; iLike.Lambda(); } } //1.定义一个函数式接口 interface ILike { void Lambda(); } //2.实现类 static class Like implements ILike { @Override public void Lambda() { System.out.println("i like lambda"); } } }
public class TestLambda01 {
public static void main(String[] args) {
//1.lambda表示简化
Ilove ilove=(int a)->{
System.out.println("i jj"+a);
};
//简化1.参数类型
ilove=(a)->{
System.out.println("i love you "+a);
};
//简化2.简化括号
ilove=a->{
System.out.println("i love you "+a);
};
//简化3.去掉花括号
ilove=a-> System.out.println("i love you "+a);
/*
总结:
lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹。
前提是函数式接口
多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
*/
ilove.love(521);
}
}
interface Ilove{
void love(int a);
}
静态代理
//静态代理模式总结:
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
/*
好处:代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情
*/
public class StaticProxy {
public static void main(String[] args) {
You you=new You();//你要结婚
new Thread(()-> System.out.println("我爱你")).start();
new WeddingCompany(new You()).HappMarry();
}
}
interface Marry{
void HappMarry();
}
//真实角色
class You implements Marry{
@Override
public void HappMarry() {
System.out.println("你要结婚了,超开心");
}
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
//代理谁--》真实目标角色
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappMarry() {
before();
this.target.HappMarry();//这就是真实对象
after();
}
private void after() {
System.out.println("结婚之后,收尾款");
}
private void before() {
System.out.println("结婚之前,布置现场");
}
}
线程状态
停止线程
//测试stop
//1.建议线程正常停止--->利用次数,,不建议死循环
//2.建议使用标志位---->设置一个标志位
//3.不要使用stop或destroy等过时或者JDK不建议使用的方法
public class TestStop implements Runnable{
//1.设置一个标识位
private boolean flag=true;
@Override
public void run() {
int i=0;
while (flag){
System.out.println("run...Thread"+i++);
}
}
//2.设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
TestStop testStop=new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i==900){
//调用stop方法切换标志位,让线程停止
testStop.stop();
System.out.println("该线程停止了");
}
}
}
}
线程休眠
-
每一个对象都有一个锁,sleep不会释放锁
-
//模拟倒计时,,, public class TestSleep01 { public static void main(String[] args) { //打印当前系统时间 Date date=new Date(System.currentTimeMillis()); while (true){ try { System.out.println(new SimpleDateFormat("HH:mm:ss").format(date)); Thread.sleep(1000); date=new Date();//更新当前时间 } catch (InterruptedException e) { e.printStackTrace(); } } } public static void tenDown() throws InterruptedException { int num=10; while (true){ Thread.sleep(1000); System.out.println(num--); if(num<=0){ break; } } } }
线程礼让
将线程从运行状态转为就绪状态,礼让不一定成功,看cpu。
//测试礼让线程
//礼让不一定成功,看cpu心情
public class TestYield {
public static void main(String[] args) {
MyYield myYield=new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
Join
Join合并线程,待此线程执行完成后,在执行其它线程,其它线程阻塞。
//测试Join方法,想象成插队
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i <100; i++) {
System.out.println("线程vip来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
TestJoin testJoin=new TestJoin();
Thread thread=new Thread(testJoin);
thread.start();
//主线程
for (int i = 0; i < 1000; i++) {
if (i==200){
thread.join();//插队
}
System.out.println("main"+i);
}
}
}
观测线程状态
线程中断或者结束,一旦进入死亡状态,就不能再次启动。
//观察测试线程的状态
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("///////");
});
//观察状态
Thread.State state=thread.getState();
System.out.println(state);//NEW
//观察启动后
thread.start();//启动线程
state=thread.getState();
System.out.println(state);//Run
while (state!=Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
Thread.sleep(100);
state=thread.getState();//更新线程状态
System.out.println(state);//输出状态
}
}
}
线程优先级
优先级第只是意味着获得调度的概率低。
//测试线程的优先级
public class TestPriority {
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority myPriority=new MyPriority();
Thread t1=new Thread(myPriority);
Thread t2=new Thread(myPriority);
Thread t3=new Thread(myPriority);
Thread t4=new Thread(myPriority);
Thread t5=new Thread(myPriority);
//先设置优先级,再启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10
t4.start();
t5.setPriority(8);
t5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
守护线程
线程分为用户线程和守护线程,守护线程如后台记录操作日志,监控内存,垃圾回收等待。
//测试守护线程
//上帝守护你
public class TestDaemon {
public static void main(String[] args) {
God god=new God();
You01 you01=new You01();
Thread thread=new Thread(god);
thread.setDaemon(true);//默认false表示是用户线程,正常的线程都是用户线程
thread.start();//上帝守护线程启动
new Thread(you01).start();//你 用户线程启动。。。
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝保佑着你");
}
}
}
//你
class You01 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你一生都开心的活着");
}
System.out.println("=========goodbye!world==============");
}
}
线程同步
多个线程操作同一个资源。并发问题。
解决线程安全的形成条件:队列+锁
线程不安全的三个例子:
同步方法
//不安全的买票
//线程不安全,有负数
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket=new BuyTicket();
new Thread(buyTicket,"我").start();
new Thread(buyTicket,"老师").start();
new Thread(buyTicket,"黄牛").start();
}
}
class BuyTicket implements Runnable{
//票
private int ticketNums=10;
boolean flag=true;
@Override
public void run() {
//买票
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized 同步方法,锁的是this
public synchronized void buy() throws InterruptedException {
//判断是否有票
if(ticketNums<=0){
flag=false;
return;
}
//模拟延迟
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
同步块
//不安全的取钱
//两个人去银行取钱,账户
public class UnsafeBank {
public static void main(String[] args) {
Account account=new Account(100,"结婚基金");
Drawing you=new Drawing(account,50,"你");
Drawing girlFriend=new Drawing(account,100,"girlFriend");
you.start();
girlFriend.start();
}
}
//账户
class Account{
int money;//余额
String name;//卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread{
Account account;//账户
//取了多少钱
int drawingMoney;
//现在手里有多少钱
int nowMoney;
public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account=account;
this.drawingMoney=drawingMoney;
}
//取钱
//synchronized 默认锁的是this
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
CopyOnWriteArrayList
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
死锁
-
死锁:多个线程互相抱着对方需要的资源,然后形成僵持。
-
产生死锁的四个必要条件:互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对以获得的资源保持不放。
不剥夺条件:进程以获得的资源,在未使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup g1=new Makeup(0,"灰姑凉");
Makeup g2=new Makeup(1,"白雪公主");
g1.start();
g2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup extends Thread{
//需要的资源只有一份,用static来保证只有一份
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;//选择
String girlName;//使用化妆品的人
Makeup(int choice,String girlName){
this.choice=choice;
this.girlName=girlName;
}
Lock
ReentranLock类:可重入锁实现Lock接口
//测试Lock
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2=new TestLock2();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}
}
class TestLock2 implements Runnable{
int ticketNums=10;
//定义Lock锁
private final ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();//加锁
if (ticketNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
}else{
break;
}
}finally {
//解锁
lock.unlock();
}
}
}
}
synchronized与Lock的对比
-
Lock是显式锁(手动开启和关闭锁,别忘了关闭锁) synchronized是隐式锁,出了作用域自动释放。
-
Lock只有代码块锁,synchronized有代码块锁和方法锁。
-
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)。
-
优先使用顺序:Lock>同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)。
线程协作
管程法
//测试:生产者与消费者模型-->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
public static void main(String[] args) {
SynContain synContain=new SynContain();
new Productor(synContain).start();
new Consumer(synContain).start();
}
}
//生产者
class Productor extends Thread{
SynContain contain;
public Productor(SynContain contain){
this.contain=contain;
}
//生产



