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;
}
//生产