Java SE -入门笔记 [toc]
0. 前置知识 文档:
0.1 Java 的文件结构 :
项目 -> 模块(包)-> .java文件 -> 类
包的命名规范:
由字母、数字下划线组成,不能以数字开头,字母全部小写。
不同路径通过点(.)来分割,如:java.lang
为了保证包名唯一性,一般使用域名反写来命名包,如:com.baidu.music
新建包:project -> 右键 src -> new -> package
按包名展开:左侧导航栏 -> 齿轮 -> 去掉 compact middle packages的勾
0.2 运行环境介绍
JVM:运行,java虚拟机
JRE:运行,(JVM+lib类库 =》真正可以执行.class文件)
JDK:开发,4个主要的文件夹:bin、include、lib、 jre
范围:
JDK > JRE >JVM
JDK
是用于java程序的开发
,而JRE
则是只能运行class而没有编译的功能
。
Java的能够“一次编译,到处运行”的原因:
Java虚拟机在执行字节码(.class文件)时,把字节码解释成具体平台上的机器指令执行。
0.3 cmd中的Java指令
1. 环境配置 + 快捷键 1.1 JDK-下载:
1.2 IDEA下载:
1.2.1 IDEA的配置:
新建Project时,需要选择已安装的JDK
更改字号:file -> settings -> editor -> font 或者 general->mouse control -> 勾选change font size whith ctrl mouse
更改快捷键【例如】:file -> settings -> keymap -> 齿轮图标 -> duplicate -> 展开 main Menu -> 展开 code -> code complete -> 展开 -> 选中 -> 右键 -> add keyboard shutcut -> 输入快捷键 -> ok
方法分隔符:file -> settings -> editor -> general -> appearance -> show method seperator
1.2.2 常用快捷键:
功能
快捷键
打印
sout
main 函数
psvm 或 main
代码提示
alt + /
自动导包(修正代码)
alt + enter
复制光标所在行,并插入到光标的下一行
ctrl + d
删除一行
ctrl + y
格式化代码
ctrl + alt +L
单行注释
ctrl + /
多行注释
ctrl + shift + /
自动生成代码(get / set /toString)
alt + insert
移动当前代码行
alt + shift + 上下箭头
快速写 遍历数组的代码
数组名.fori + enter
快速生成代码块,如:for、if、try-catch
ctrl + alt + T
快速生成 for 代码块
fori
快速生成 增强型 for
iter 或者 foreach
搜索类
ctrl + n
查看子类
ctrl + h
2. 数据类型
2.1 char 和 byte 的差别:
char 是无符号型的,可以表示一个整数,不能表示负数;char可以表中文字符,
byte 是有符号型的,可以表示 -128—127 的数,byte不可以表中文字符
3. 运算符
4. 数组
数组 直接打印: 得到的是地址
数组反转:对称位置的元素交换
4.1 数组的初始化:【4种】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 int [] arr = new int []{1 ,2 ,4 ,6 }; int [] arr = new int [10 ]; arr[0 ] =12 ; arr[1 ] = 15 ; arr[2 ] = 45 ; int [] arr = {1 ,2 ,5 ,6 }; int [] arr; arr = new int [10 ]{1 ,2 ,5 ,7 ,9 };
5. 内存区域的划分
存放方法的局部变量,运行方法 【局部变量 的特点:一旦超出作用域就会从内存中消失】
new出来的东西都放在堆中(为:引用类型),如 :数组 【堆内的东西都有一个16进制的地址值】
存储.class
的相关信息,包含:方法信息
本地方法栈(native method stack)
6. 面向对象 面向对象的三大特性:
封装性(如: private, 方法等)
继承性(extend ,super等)
多态性(子类 继承父类 的方法后可以 覆盖重写 override)
6.1 类:
类
= 属性
+ 方法
// 方法也就是行为、函数。
JAVA的类
:成员变量
(在类内部,即:属性) + 成员方法
+构造方法
// 普通变量:写在函数的内部。
方法
:只能有1个返回值
。如果想返回多个数 ,可以将返回值定义为数组 ,并返回数组首地址。
JavaBean:标准类,类中至少要包括:无参构造方法
,全参构造方法
,属性 + getter + setter
构造方法:在创建对象(new)时,自动调用。【快捷键:alter + insert
】
6.1.1 构造方法:
构造方法的名称
必须与所在的类的名称
完全相同,大小写也要一样。 【普通方法首字小写,构造方法首字大写】
构造方法不能写返回值类型
,连void也不要写。
构造方法不能return
返回值。
如果没有写
构造方法,那么编译器会自动赠送
1个构造方法。
只要自己编写了构造方法
,编译器就不会赠送
构造方法。
可以在定义构造方法时,将所需的参数定义在方法的参数列表中, 可以在创建对象时传入参数。但是还是需要写getter和setter 方法
,目的是方便修改参数的值
。
6.1.2 成员变量 与 局部变量 的区别:
成员变量
:定义在 类
的内部,在类 中都可以使用 ,有默认值
。
局部变量
:定义在 方法内部
,只能在 方法内部 使用,没有默认值
,使用要先赋值 。
6.1.3 成员变量 与 局部变量 重名时 的优先级:
默认根据就近原则
,优先
使用 局部变量
。
如果想使用成员变量
,可以使用this关键字
,如: this.name
。
this
一定是写在方法内部
的,用于在变量名相同
时,做区分
。
6.1.4 Override 方法重写 + Overload 方法重载:
Override 方法重写
:
子类继承父类/接口 后,换掉同名的方法中的处理语句。
方法重写前后:返回值类型、方法名、参数必须一致。
重写后,访问权限必须更宽松,如:重写前 => protected,重写后 => public
Overload 方法重载
:
在一个类里面,返回类型可以相同也可以不同,方法名一致,而参数不同。
覆盖重写Override:【需要先:继承】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 public class Father { private String name; public Father (String name) { this .name = name; } public Father () { } public String getName () { return name; } public void setName (String name) { this .name = name; } } public class Son extends Father { public Son (String name) { super (name); } public Son () { } @Override public void eat () { System.out.println("son:我要吃 -> 水果" ); } } public class FaterAndSonDemo { public static void main (String[] args) { Father father = new Father("刘备" ); Son son = new Son("阿斗" ); father.eat(); son.eat(); } }
覆盖重载Overload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Calc { private int a; private int b; private float c; public int sum (int a, int b) { return a+b; } public float sum (int a,int b,float c) { return a+b+c; } }
6.1.5 内部类:【4种】
内部类
:1个类 定义在 另一个类 的内部 =》内部类
(public / protected / private)。 外面的类叫外部类
(只能public)。
注意 :内部类
可以访问外部类
的所有成员变量
和成员方法
。
成员内部类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class OutAndInnerClassDemo { public static void main (String[] args) { Outer outer = new Outer(); Outer.Inner inner = outer.new Inner () ; } } class Outer { public Outer () { System.out.println("外部类" ); } class Inner { public Inner () { System.out.println("内部类" ); } } }
静态内部类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class OutAndInnerClassDemo2 { public static void main (String[] args) { Outer2.Inner inner = new Outer2.Inner(); } } class Outer2 { public Outer2 () { System.out.println("外部类" ); } static class Inner { public Inner () { System.out.println("静态内部类" ); } } }
方法内部类:【在方法内部:定义、创建new、使用(调用)】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class OutAndInnerClassDemo3 { public static void main (String[] args) { Outer3 o3 = new Outer3(); o3.Func1(); } } class Outer3 { public Outer3 () { System.out.println("外部类 -> 创建" ); } public void Func1 () { System.out.println("外部类的方法 -> 调用" ); class Inner3 { public Inner3 () { System.out.println("内部类 -> 创建" ); } public void say () { System.out.println("内部类的方法 -> 调用" ); } } Inner3 i3 = new Inner3(); i3.say(); } }
匿名内部类:【方法中 return 1个 new出的对象】
注意 :匿名内部类 没有 构造方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class OutAndInnerClassDemo4 { public static void main (String[] args) { Outer4 o4 = new Outer4(); o4.getInner4().eat(); } } public interface Inner4 { void eat () ; } class Outer4 { public Outer4 () { System.out.println("外部类 -> 创建" ); } public Inner4 getInner4 () { return new Inner4(){ @Override public void eat () { System.out.println("匿名内部类 -> 方法" ); } }; } }
6.1.6 类的使用:
导包:import java.util.*;
创建:Animals cat = new Animals();
使用:cat.eat();
注意:
6.1.7 静态变量 与 静态方法:
静态变量
:带有 static
关键字的变量
静态方法
:带有 static
关键字的方法
注意 :【凡是带static
的变量、方法、代码块
】
属于
整个类
,而不仅仅 属于一个对象
只创建1次
可以直接用 类名.变量
调用
不能使用 this
,super
关键字:因为静态方法可以通过类名来调用,而这是可能还没有创建对象,更谈不上继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static { } public static void PrintMe () {.......} 类名称.静态变量; 类名称.静态方法();
6.2 接口:
接口就是各个类的规范,
接口是一种 引用的数据类型
,其中:最重要的是抽象方法
6.2.1 格式: 1 2 3 public interface 接口名称 { }
6.2.2 接口内容:
JDK 1.7 的接口可以包含:
JDK 1.8 的接口还可以额外包含
JDK 1.9 的接口还可以额外包含
注意 :
接口中的抽象方法,修饰符必须是 public abstract
【如:public abstract void methodAbstract( );
】, 可以省略但不能换成别的 。
接口
无法直接使用,只能让1个 “实现类” 来 “实现” 该接口 。
【类似 抽象类的继承】
实现类
除非是1个由abstract修饰的抽象类,否则必须覆盖重写
所有抽象方法
。
6.2.3 接口的使用: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public interface MyIntface { public abstract void Say () ; } public class ToDoClass implements MyIntface { @Override public void Say () { System.out.println("你好!" ); } } public class IntfaceDemo { public static void main (String[] args) { ToDoClass a = new ToDoClass(); a.Say(); } }
6.3 四大特性: 面向对象的三大特性:
封装性(如: private, 方法等)
继承性(extend ,super等)
多态性(子类 继承父类 的方法后可以 覆盖重写 override)
6.3.1 封装性: 封装性有两种:【变量,方法】
private关键字:
只有在写了private 的类中可以直接访问该属性,
有private 关键字
就必须在类中定义
2个专门 访问、设置 该属性的方法 getter、setter
。
setNum(int num)【有参数,无返回值】
getNum( )【无参数,有返回值】
6.3.2 继承性: 继承性:
继承是多态性的前提
;
承解决了共性的问题(把几个类
中都有的属性、方法
放到1个父类中
,每个子类 继承 父类
)
父类与子类:
父类:基类,超类
子类:派生类
在继承关系中,子类就是父类的一种。
例如: 父类是员工,子类是老师,老师也是一种员工 =》 老师 is 员工
继承过程中,成员变量的访问特点:
子类有,则优先使用 子类的成员变量
。
子类
知道 父类的变量、方法
,父类不知道子类的变量、方法。
父子的变量、方法
重名时,优先使用子类
父类的变量
,子类的变量
,方法的局部变量
重名时:
访问 父类的变量:super.变量名
访问 本类的变量: this.成员变量名
访问 方法的局部变量: 直接写变量名
父类、子类、继承的使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class 父类名称 {} public class 子类名称 extends 父类名称 {} public class Employee { String name; int age; } public class Teacher extends Employee { String Tno; }
6.3.3 抽象性: 抽象:
一个父类有一个没法具体描述的方法: 如:
子类 cat
有 eatFish( )
的方法,
父类 Animal
有eat( )
的方法,但没法细说具体执行过程。
抽象方法-格式:
1 2 3 4 5 public abstract void Eat () ;
注意 :
抽象方法
只能 定义在抽象类内
【在 class前加上 abstract】,但 抽象类
不一定有 抽象方法
,没有抽象方法的 抽象类 同样 不能直接new,它有特殊用途。
子类 必须 覆盖重写 父类 所有的 抽象方法。 【除非子类也是抽象类】
抽象方法覆盖重写 即:去掉 抽象方法的 abstract关键字,补上{ }
【可以鼠标点击 extends 行 按下 alt + enter】
抽象类:
1 2 3 4 5 6 7 public abstract class Animals { public abstract void Eat () ; }
抽象类
与 抽象方法
的使用:
抽象类的 对象 无法直接 new 。
即: 不能 Animal a = new Animal( );
必须先
用一个子类 继承
一个父类,再覆盖重写
其抽象方法。
main方法中
使用抽象类
的执行顺序
:
子类 调用 super( ),执行抽象父类构造方法。
执行 子类构造方法。
执行 子类的抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public abstract class Animals { public abstract void Eat () ; } public class Cat extends Animals { @Override public void Eat () { System.out.println("cat eat" .toUpperCase()); } } public class Demo { public static void main (String[] args) { Cat c = new Cat(); c.Eat(); } }
6.3.4 多态性:
多态性的条件:
实现多态的技术:
动态绑定(dynamic binding),是指在执行期间判断
所引用对象的实际类型
,根据其实际的类型调用其相应的方法。
1 2 3 4 5 6 List<Integer> arr = new ArrayList<>();
6.4 包装类
6.4.1 包装类是什么:【自动装箱 + 自动拆箱】 包装类 = 基本类型 + 常用方法
将一个类型变成一个类。
原始数据类型
=转换为=》对象
对象
=转换为=》 原始数据类型
可用于实现 多态性
*JDK1.5
*开始可以进行自动装箱,自动拆箱
:
自动装箱: 自动将 基本类型 => 包装类
自动拆箱: 自动将 包装类 => 基本类型
6.4.2 泛型: 泛型 <=> <包装类>
泛型常用于:指定某个集合
只能保存某种数据类型的数据
。
1 2 3 List<String> list = new ArrayList<String>();`
7. 常用的类 7.1 Math类: Math的所属路径:java.lang.Math
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.cyw2;public class Demo { public static void main (String[] args) { double a = Math.floor(1.4 ); double b = Math.round(1.4 ); double c = Math.ceil(1.4 ); System.out.println("1.4 => floor " +a); System.out.println("1.4 => round " +b); System.out.println("1.4 => ceil " +c); System.out.println("==============" ); double d = Math.random(); System.out.println(d); int max = Math.max(12 ,34 ); int min = Math.min(12 ,34 ); System.out.println(max); System.out.println(min); System.out.println("==============" ); double e = Math.toRadians(45.0 ); double f = Math.tan(e); double g = Math.rint(f); System.out.println(g); System.out.println("==============" ); String h = Integer.toString(123 ); System.out.println(h); String i = "123" ; int j = Interager.parseInt(i); System.out.println(j); System.out.println("==============" ); double k = Math.sqrt(100.0 ); double L = Math.pow(10.0 ,2.0 ); System.out.println(k); System.out.println(L); } }
7.2 Charater类: 常用方法:
判断
字符类型:是否是 字母、数字、空白符、大写、小写
转化大小写:转化成 大写、小写、字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 char ch = 'a' ; Character ch = new Character('a' ); char uniChar = '\u039A' ; char [] charArray ={ 'a' , 'b' , 'c' , 'd' , 'e' }; Character ch = new Character('a' ); char a = 'A' ; boolean isLetter = Character.isLetter(a); System.out.println(isLetter); System.out.println(Character.isDigit(a));
7.3 String类: String 类是不可改变的,一旦创建了 String 对象,那它的值就无法改变了。
要想改变 String 对象的值
,可以使用 StringBuffer
和 StringBuilder
类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 String str = "Runoob" ; String str2 = new String("Runoob" ); str = "abc" ; int len = str.length(); System.out.println(len); String str2 = "我的名字是 " .concat("Runoob" ); String str3 = "我的名字是 " +"Runoob" ; String fs; fs = String.format("a的值为%f, 整型变量的值为" %d, 字符串变量的值为 %s", floatVar, intVar, stringVar); // 返回指定位置的字符 String str1 = " abc"; char ch = str1.charAt(0); System.out.println(cg); // a // 返回字符串中指定字符的第一个索引 String str1 = " abc"; int index = str1.indexOf('b'); System.out.println(index); // 1 // 截取从参数位置到最后的字符串: substring(索引) // 截取从[索引1,索引2)的字符串: substring(索引1,索引2) String str1 = " hello"; String str2 = str1.substring(0); //str2 = " hello" String str3 = str1.substring(1,4); //str3 = " ell"; // 字符串的转化 //将字符串拆为字符数组,并将字符数组作为返回值: toCharArray(); //获取当前字符串底层的字节数组: getBytes(); //替换字符串,返回新字符串: replace(old,new); //字符串的分割 //split(正则表达式):按指定规则分割字符串,并返回字符数组,若想用. 作为分割符,需要写 \\.
字符串在内存中的创建过程:
以 String str1 = “abc” 为例
内存中情况:
在 栈
中开辟 变量的空间
,
在 堆
中的 字符串常量池
中创建String 对象
在 堆
中 创建 byte[]
String 对象
引用 byte[] 的地址
栈
中的变量 引用 String 对象 的地址
7.4 StringBuffer 类 与 StringBuilder 类: StringBuffer 和 StringBuilder 都可用来创建字符串, 区别:
StringBuffer :速度慢,但在多线程时安全
StringBuilder :速度快,但在多线程时不安全【StringBuilder 更常用】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Demo { public static void main (String args[]) { StringBuilder sb_1 = new StringBuilder(10 ); sb_1.append("大家好" ); System.out.println(sb_1); sb.insert(8 , "Java" ); System.out.println(sb_1); sb.delete(5 ,8 ); System.out.println(sb_1); } }
7.5 Date类: import java.util.Date;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Date d1 = new Date(); Date d1 = new Date(long millisec); A.before(B); A.after(B); A.equals(B); Date dNow = new Date( ); SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" ); System.out.println("当前时间为: " + sdf.format(dNow));
7.6 Calender类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Calendar c = Calendar.getInstance(); Calendar c1 = Calendar.getInstance(); c1.set(2009 , 6 - 1 , 12 ); Calendar c1 = Calendar.getInstance(); int year = c1.get(Calendar.YEAR); int month = c1.get(Calendar.MONTH) + 1 ; int date = c1.get(Calendar.DATE); int hour = c1.get(Calendar.HOUR_OF_DAY); int minute = c1.get(Calendar.MINUTE); int second = c1.get(Calendar.SECOND); int day = c1.get(Calendar.DAY_OF_WEEK);
7.7 Scanner 类: import java.util.Scanner;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Scanner sc1 = new Scanner(System.in); if (sc1.hasNext()) { String str1 = sc1.next(); System.out.println("输入的数据为:" + str1); } sc1.close(); Scanner sc2 = new Scanner(System.in); System.out.println("nextLine方式接收:" ); if (sc2.hasNextLine()) { String str2 = sc2.nextLine(); System.out.println("输入的数据为:" + str2); } scan.close();
7.8 Regex-正则表达式: import java.util.regex.*;
java.util.regex 包主要包括以下三个类:
Pattern 类:表达式
Matcher 类:匹配引擎
PatternSyntaxException类:语法错误
Java中的\\
等价于 其他语言中的 \
:
\b
表示匹配删除,\\b
表示匹配边界
例如:要匹配 str1 = ”(hello)“,正则必须为\\(hello\\)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 String str1 = "Hello World 123,Hello Man 456! " ; String pattern = ".*Hello.*" ; boolean isMatch = Pattern.matches(pattern,str1); System.out.println("字符串中是否能否找到Hello:" +isMatch);
7.9 try-catch 异常处理: 所有的异常类是从 java.lang.Exception 类
继承的子类
Java 异常处理 | 菜鸟教程 (runoob.com)
异常:
检查性异常
:最容易犯,编译时不能忽略,如:打开不存在的文件
运行时异常
:可以避免,编译时可被忽略,如:空指针异常、下标越界异常、算数异常(如:除数为0)、类型转化异常
错误
(编译错误、运行错误、逻辑错误):脱离控制,如:堆栈溢出、虚拟机错误、线程死锁
声明异常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 try { file = new FileInputStream(fileName); x = (byte ) file.read(); } catch (FileNotFoundException f) { f.printStackTrace(); return -1 ; } catch (IOException i) { i.printStackTrace(); return -1 ; } finally { } import java.io.*;public class className { public void deposit (double amount) throws RemoteException { throw new RemoteException(); } }
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类
【编译不通过的异常】,则需要继承 Exception 类。=》try-catch 或 throws
如果你想写一个运行时异常类
【运行不通过的异常】,那么需要继承 RuntimeException 类。
出现算数异常:除数不能为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Demo { public static void main (String[] args) { int a=10 ,b=0 ; System.out.println(div(a,b)); } public static int div (int a,int b) { return a/b; } }
自定义异常-步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public class myException extends Exception { public myException () { super (); } public myException (String msg) { super (msg); } } public class Father { public void eat () throws myException { System.out.println("我是父类的-eat" ); } } public class Daughter extends Father { public static void main (String[] args) { Father daughter = new Daughter(); try { daughter.eat(); }catch (myException e){ e.printStackTrace(); } } @Override public void eat () throws myException { System.out.println("我是女儿类的-eat" ); System.out.println("我是女儿类的-eat" ); System.out.println("我是女儿类的-eat" ); throw new myException("我吃太多了~" ); } }
8. Stream、IO、File import java.io.*;
需要定义 IO异常
8.1 Stream的介绍: 流-Stream
:
一个数据的序列
。【以 内存 为参照物】
字节流:可以复制 文本、图片、二进制文件
字符流:可以复制 文本文件(包括中文)
流的分类:
按方向:
输入流:读取数据 => 内存
输出流:写入数据 => 存储设备
按单位:
按功能:
8.2 Stream的常用类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 import java.io.FileInputStream; public class Demo { public static void main (String[] args) throws Exception { FileInputStream fis = new FileInputStream("e:\\abc.txt" ); int data; while ( (data=fis.read()) != -1 ){ System.out.println(data); } fis.close(); System.out.println("读取完毕!" ); byte [] buf = new byte [3 ]; int count = 0 ; while ( (count = fis.read(buf) ) !=-1 ){ System.out.println(new String(buf)); } fis.close(); System.out.println("读取完毕!" ); FileOutputStream fos = new FileOutputStream("e:\\abc.txt" ); fos.write(97 ); byte [] buf = new byte []{'d' ,'e' ,'f' }; fos.write(buf); String str1 = "这是一个字符串" ; byte [] buf2 = str1.getBytes(); fos.write(buf2); fos.close(); System.out.println("输出完毕" ); } }
8.3 案例-文件复制: 【先输入流-读取,再输出流-写入】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.io.FileInputStream;import java.io.FileOutputStream;public class Demo { public static void main (String[] args) throws Exception { FileInputStream fis= new FileInputStream("E:\\A.jpeg" ); FileOutputStream fos = new FileOutputStream("E:\\0\\B.jpeg" ); byte [] buf = new byte [67 *1024 ]; int count = 0 ; while ((count = fis.read(buf)) != -1 ) { fos.write(buf,0 ,count); } fis.close(); fos.close(); System.out.println("复制完毕" ); } }
8.4 字节缓存流: java.io.BufferedInputStream
java.io.BufferedOutputStream
需要与 InputStream或 其子类 配合使用。
目的:
调高IO效率,减少访问磁盘
数据存储在缓冲区,flush( )将数据写入文件,也可直接close( )
【close( )也会调用flush( ),将内容写入文件】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 FileInputStream fis = new FileInputStream("e:\\abc.txt" ); BufferedInputStream bis = new BufferedInputStream(fis); int data=0 ; while ((data = bis.read()) !=-1 ){ System.out.print(data+":" +(char )data+" " ); } bis.close(); fis(); FileOutputStream fos = new FileOutputStream("e:\\def.txt" ); BufferedOutputStream bos = new BufferedOutputStream(fos); int i=0 ; while (i<10 ){ i++; bos.write("Hello\n" .getBytes()); bos.flush(); } bos.close(); fos();
8.5 对象流(序列化 / 反序列化):
ObjectOutputStream
ObjectInputStream
对象流:【需要结合 InputStream / OutputStream】
增强了 缓冲区的功能
增强了 读写 基本数据类型 + 字符串
增强了 读写对象:readObject( ) 和 writeObject( obj)
使用 Stream 传输 Object 的过程
:
8.5.1 序列化:【写入Object 到 文件】 注意 :
被 写入文件(序列化)的类 必须实现 可序列化接口
。
如果某个属性不想被序列化
,可以使用transient来修饰 某个类里 的 某个属性:private transient int age;
。
静态属性
不可以序列化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class Student implements Serializable { private static final long serialVersionUID = 100L ; private String name; private int age; } FileOutputStream fos = new FileOutputStream("e:\\ghi.txt" ); ObjectOutputStream oos = new ObjectOutputStream(fos); Student ZhangSan = new Student("张三" ,20 ); oos.writeObject(ZhangSan); oos.close();
8.5.2 反序列化:【读取文件中的Object】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 FileInputStream fis = new FileInputStream("e:\\ghi.txt" ); ObjectInputStream ois = new ObjectInputStream(fis); Student stu1 = (Student) ois.readObject(); System.out.println(stu1.getName()); System.out.println(stu1.getAge()); ois.close();
8.6 字符流: 8.6.1 字符编码:
ISO-8859-1
:【1个Byte表示】ASCII,希腊语、阿拉伯语、泰语等
UTF-8
:UniCode的可变长编码
GB-2312
:【1-2 Btye】简体中文
GBK
:【1-2 Btye】简体中文 + 扩展(GB-2312的升级版)
Big5
:台湾-繁体中文
注意: 编码方式
与 解码方式
不一致 =》乱码
8.6.2 字符流: 字符流 =》 java.io.Reader
和 java.io.Writer
字符流常用子类:FileReader
和 FileWriter
FileReader:【可显示中文(默认UTF-8编码)】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 FileReader fr = new FileReader("e:\\0\\1.txt" ); int data= 0 ; while ( (data=fr.read())!=-1 ){ System.out.println((char )data); } fr.close(); FileReader fr = new FileReader("e:\\0\\1.txt" ); char [] buf = new char [4 ]; int count= 0 ; while ( (count = fr.read(buf)) != -1 ){ System.out.println(new String(buf,0 ,count)); } fr.close();
FileWriter:
1 2 3 4 FileWriter fw = new FileWriter("e:\\0\\1.txt" ); fw.write("天天向上" ); fw.close();
8.6.2 字符流-复制文本文件: FileReader + FileWriter
: 复制文本文件 【无法复制:图片、二进制文件
】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 FileReader fr = new FileReader("e:\\1.txt" ); FileWriter fw = new FileWriter("e:\\0\\2.txt" ); char [] buf = new char [1024 ]; int count = 0 ; while ((count = fr.read(buf)) != -1 ){ fw.write(new String(buf,0 ,count)); fw.flush(); } fw.close(); fr.close();
8.6.3 字符缓冲流:
BufferedReader:输入
BufferedWriter:输出【原样打印】
PrintWriter:输出【原样打印、换行打印】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 FileReader fr = new FileReader("e:\\1.txt" ); BufferedReader br = new BufferedReader(fr); char [] buf = new char [1024 ]; int count =0 ; while ((count=br.read(buf))!= -1 ){ System.out.println(buf); } FileReader fr = new FileReader("e:\\1.txt" ); BufferedReader br = new BufferedReader(fr); char [] buf = new char [1024 ]; String line = null ; while ((line=br.readLine()) != null ){ System.out.println(line); } FileWriter fw = new FileWriter("e:\\1.txt" ); BufferedWriter bw = new BufferedWriter(fw); bw.write("好好学习" ); bw.newLine(); bw.close(); PrintWriter pw = new PrintWriter("e:\\2.txt" ); pw.println(97 ); pw.println(true ); pw.flush();
8.7 桥转换流: java.io.InputStreamReader
和 java.io.OutputStreamWriter
转化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 FileInputStream fis = new FileInputStream("e:\\1.txt" ); InputStreamReader isr = new InputStreamReader(fis,"utf-8" ); int data=0 ; while ((data= isr.read())!=-1 ){ System.out.println((char )data); } System.out.println(isr.getEncoding()); isr.close(); fis.close(); FileOutputStream fos = new FileOutputStream("e:\\1.txt" ); OutputStreamWriter osw = new OutputStreamWriter(fos); osw.write("霓虹,你好\r\n我是第二行" ); osw.flush(); fos.close();
8.8 File类: File类
:代表物理磁盘中的文件、文件夹
。
File类-使用:
分隔符
File.pathSeparatorChar:路径分隔符(\)
File.separator:名称分隔符(;)
文件操作
文件夹操作
8.8.1 文件操作: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public static void creFile () throws Exception { File file = new File("e:\\3.txt" ); boolean isCreSuccess = file.createNewFile(); System.out.println(isCreSuccess); System.out.println(file.exists()); System.out.println(file.delete()); file.deleteOnExit(); System.out.println(file.getAbsoluteFile()); System.out.println(file.getPath()); System.out.println(file.getName()); System.out.println(file.getParent()); System.out.println(file.length()); System.out.println(new Date(file.lastModified()).toString()); System.out.println(file.canRead()); System.out.println(file.canWrite()); System.out.println(file.canExecute()); System.out.println(file.isFile()); System.out.println(file.isHidden()); }
8.8.2 文件夹操作: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public static void creDir () throws Exception { File dir =new File("e:\\0\\1" ); if (!dir.exists()){ dir.mkdirs(); } dir.delete(); String[] arr = dir.list(); for (String f : arr){ System.out.println(f); } }
8.8.3 FileFilter接口: 【按条件 筛选出 文件、文件夹】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 File[] arr = dir.listFiles(new FileFilter() { @Override public boolean accept (File pathname) { if (pathname.getName().endsWith(".txt" )){ return true ; }else { return false ; } } }); for (File f:arr){ System.out.println(f.getName()); }
9. 集合框架
9.1 引入: 数组、链表、集合
等,都是存放多个数据
的1种容器,都用于在内存中存储
(而不是持久化存储:txt,abi,jpg)。
数组
:【有序存储,元素可重复】
特点:指定长度后,长度不可再次更改,只能存放同一种类型的数据。 int [ ] arr = new int[ ];
缺点:长度固定,不可更改;添加、删除元素时,效率低;没有现成的方法或属性来获取数组长度
为解决数组
的上述缺点
,引入了集合
。
=> 必须自动装箱、自动拆箱
来将 基本类型
转化为 引用类型
。
不同的集合,在底层的数据结构的实现不同。
数组长度固定,集合长度不固定。
数组可以存储基本类型+引用类型,集合只能存储引用类型(因此需要 自动装箱、自动拆箱)。
集合的包的位置:import java.uitl.*;
9.2 Collection集合的操作: Collection、List、Set是接口,不能直接new,而要借助他们的子类如:ArrayList、LinkedList、HashSet、TreeSet。
9.2.1 Collection 集合接口: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 Collection coll = new ArrayList(); coll.add("苹果" ); coll.add("西瓜" ); System.out.println(“元素个数:”+coll.size()); System.out.println(coll); coll.remove("苹果" ); System.out.println(coll); coll.clear(); System.out.println(coll); for (Object obj:coll) { String s = (String)obj; System.out.println(obj); } Iterator it = coll.iterator(); while (it.hasNext()){ String s = (String)it.next(); System.out.println(s); it.remove(); } System.out.println(coll.size()); System.out.println(coll.contains("香蕉" ));、 System.out.println(coll.isEmpty(香蕉));
9.2.2 List 集合子接口: 有序、可重复、有下标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 import java.util.List;import java.util.ListIterator; List list = new ArrayList<>(); list.add("苹果" ); list.add("西瓜" ); list.add("香蕉" ); System.out.println(list); ListIterator it = list.listIterator(); while (it.hasNext()){ System.out.println(it.next()); } while (it.hasNext()){ it.next(); } while (it.hasPrevious()){ System.out.println(it.previous()); } System.out.println(list.indexOf("香蕉" )); List list = new ArrayList<>(); list.add(20 ); list.add(30 ); list.add(40 ); list.add(50 ); list.add(60 ); System.out.println(list); list.remove((Object)20 ); list.remove(0 ); System.out.println(list);
9.2.3 List 接口的常用实现类:
ArrayList【重点】:数组,查询快、增删慢,线程不安全【jdk1.2】
源码分析:
默认容量(default_capacity):当没有元素时,0;有1个元素时,10
数组(elementData)
当前大小(size):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 LinkedList ll = new LinkedList(); Student s1 = new Student("张1" ,10 ); Student s2 = new Student("张2" ,20 ); Student s3 = new Student("张3" ,30 ); ll.add(s1); ll.add(s2); ll.add(s3); System.out.println(ll); ll.remove(s2); System.out.println(ll); ll.addFirst(s2); System.out.println(ll); ll.removeFirst(); System.out.println(ll); ll.addLast(s2); System.out.println(ll); ll.removeLast(); System.out.println(ll); ListIterator it = ll.listIterator(); while (it.hasNext()){ System.out.println(it.next()); } while (it.hasPrevious()){ System.out.println(it.previous()); }
Vector:数组,查询快、增删慢,线程安全,【jdk1.0】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 Vector v = new Vector(); v.add("张三" ); v.add("里斯" ); v.add("威武" ); Enumeration en = v.elements(); while (en.hasMoreElements()){ System.out.println(en.nextElement()); } System.out.println( v.firstElement()); System.out.println( v.lastElement()); System.out.println( v.elementAt(1 ));
9.2.4 Set 集合: 只有包含 Collection 集合中的方法,没有自己额外的方法。
HashSet: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 HashSet<Student> s = new HashSet<>(); Student s1 = new Student("张1" ,10 ); Student s2 = new Student("张2" ,20 ); Student s3 = new Student("张3" ,30 ); s.add(s1); s.add(s2); s.add(s3); System.out.println(s); s.add(s3); System.out.println(s); s.remove(s2); System.out.println(s); System.out.println( s.contains(s2) ); System.out.println( s.isEmpty() ); for (Student item : s) { System.out.println(item); } Iterator<Student> it = s.iterator(); while (it.hasNext()){ System.out.println(it.next()); } @Override public int hashCode () { int n1 = this .name.hashCode(); int n2 = this .age; return n1+n2; } @Override public boolean equals (Object obj) { if (obj == this ){ return true ; } else if (obj == null ){ return false ; } else if (obj instanceof Student){ Student s = (Student) obj; if (this .name.equals(s.getName())&&this .age==s.getAge()){ return true ; } } return false ; }
TreeSet: 【红黑树,即:二叉查找树】
按 排列顺序
实现 元素不重复
实现了 SortedSet 接口
, 对元素 进行自动排序
元素类 必须实现 Comparable 接口
,指定 排序规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 @Override public int compareTo (Student o) { int n1 = this .name.compareTo(o.getName()); int n2 = this .age-o.getAge(); return n1==0 ?n2:n1; } TreeSet<Student> s = new TreeSet<>(new Comparator<Student>() { @Override public int compare (Student o1, Student o2) { int n1 = o1.getAge() - o2.getAge(); int n2 = o1.getName().compareTo(o2.getName()); return n1==0 ?n2:n1; } }); TreeSet<Student> s = new TreeSet<>(); Student s1 = new Student("张1" ,10 ); Student s2 = new Student("张2" ,20 ); Student s3 = new Student("张3" ,30 ); s.add(s1); s.add(s2); s.add(s3); System.out.println(s); s.add(s3); System.out.println(s); s.remove(s2); System.out.println(s); System.out.println( s.contains(s2) ); System.out.println( s.isEmpty() ); for (Student item : s) { System.out.println(item); } Iterator<Student> it = s.iterator(); while (it.hasNext()){ System.out.println(it.next()); }
TreeSet-案例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package CollectionDemo;import java.util.Comparator;import java.util.TreeSet;public class TreeSetDemo { public static void main (String[] args) { TreeSet<String> t = new TreeSet<>(new Comparator<String>() { @Override public int compare (String o1, String o2) { int n1 = o1.length() - o2.length(); int n2 = o1.compareTo(o2); return n1==0 ?n2:n1; } }); t.add("beijing:7" ); t.add("guangzhou:9" ); t.add("shanghai:8" ); System.out.println(t); } }
9.3 泛型 注意:6.4.2
中也提到了泛型
泛型-定义:
把数据类型
当作参数
,且传入
的数据类型必须是引用类型
【基本类型
必须使用 其包装类
作为参数】
例如:
ArrayList<Integer> arr = new ArrayList< >( );
常见形式:
语法:<T,… >
,其中的 T可以换成E,K,V
好处:
提高代码复用性
提高代码安全性,防止 类型转化异常
泛型在集合框架中的应用:
可以指定某个集合只能传入某个类型的数据。
【不指定泛型
,默认
传入Object类型
,因此需 强制转化
类型】
9.3.1 泛型类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class MyGeneral <T > { T t1; public void show (T t1) { System.out.println(t1); } public T getT1 () { return t1; } } MyGeneral<String> m1 = new MyGeneral<>("张三" ); System.out.println( m1.getT1()); m1.show("你好" ); MyGeneral<Integer> m2 = new MyGeneral<>(123 ); System.out.println(m2.getT1()); m2.show(456 );
9.3.2 泛型接口: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public interface myInt <T > { T show (T t1) ; } public class myIntClass implements myInt <String > { @Override public String show (String t1) { System.out.println(t1); return t1; } } myIntClass myInt_1 = new myIntClass(); myInt_1.show("你好" ); public class myIntClass2 <T > implements myInt <T > { @Override public T show (T t1) { System.out.println(t1); return t1; } } myIntClass2<Integer> myInt_2 = new myIntClass2<>(); myInt_2.show(123 );
9.3.3 泛型方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class myGer { public <T> void show (T t1) { System.out.println("这是泛型方法:" + t1); } } myGer ger = new myGer(); ger.show(123 ); ger.show("你好" );
9.4 Map 集合:
特点:
用于存储 无序、无下标
的键值对
键:无序、无下标、不重复
值:无序、无下标、可重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 Map<String,String> m = new HashMap<>(); m.put("a" ,"a->10" ); m.put("b" ,"b->20" ); m.put("c" ,"c->30" ); System.out.println(m); m.remove("c" ); System.out.println(m); Set<String> keyset = m.keySet(); for (String k : keyset) { System.out.println(k+"---" m.get(k)); } Set<Map.Entry<String,String>> entries = m.entrySet(); for (Map.Entry<String,String> item: entries ) { System.out.println(item.getKey()+"---" +item.getValue()); }
9.4.1 HashMap 默认容量:16。
75%时,开始扩容。
数组长度>8 且 链表长度>64时,使用红黑树。
为实现每一项的键和值都不一样,需要重写 hashcode()、equals() 【可使用IED的快捷键】
HashMap源码分析-小结:
HashMap 刚创建时,table为null【为节省空间】,当添加第一个元素时,table的容量为16。
元素个数大于阈值(容量的75%)时,会进行扩容为原来的2倍,目的是减少需要调整的元素个数。
JDK1.8 ,当每个链表长度 大于8,元素个数 大于等于64时,调整为红黑树,目的是提高元素的效率。
JDK1.8 ,当每个链表长度 小于6时,使用链表。
JDK1.8以前,使用头插法;JDK1.8以后,使用尾插法。
案例:
统计字符串中每个字符的出现次数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.util.HashMap;import java.util.Scanner;public class Demo { public static void main (String[] args) { Scanner sc = new Scanner(System.in); String s = sc.nextLine(); HashMap<Character,Integer> hm = new HashMap<>(); char [] arr = s.toCharArray(); for (char item: arr) { if (hm.containsKey(item)){ int t = hm.get(item); hm.put(item,++t); }else { hm.put(item,1 ); } } System.out.println("现在的字符串:" +s); System.out.println("现在的HashMap:" +hm); } }
9.4.2 TreeMap 存储:红黑树
对 key 自动排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Student s1 = new Student("张1" , 10 ); Student s2 = new Student("张2" , 20 ); Student s3 = new Student("张3" , 30 ); TreeMap<Student, String> tm = new TreeMap<>(); tm.put(s1, "1" ); tm.put(s2, "2" ); tm.put(s3, "3" ); System.out.println(tm); Set<Student> set = tm.keySet(); for (Student k:set ) { System.out.println(k+"---" +tm.get(k)); } Map.Entry<Student,String>> entries = tm.entrySet(); for (Map.Entry<Student,String> item: entries) { System.out.println(item.getKey()+"---" +item.getValue()); }
9.5 Collections 工具类: 方法 :
public static void reverse(List<?> list)
//反转集合中元素的顺序
public static void shuffle(List<?> list)
//随机重置集合元素的顺序
public static void sort(List<T> list)
//升序排序(元素类型必须实现Comparable接口)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ArrayList<Integer> arr = new ArrayList<>(); arr.add(10 ); arr.add(20 ); arr.add(30 ); arr.add(40 ); System.out.println(arr); Collections.shuffle(arr); System.out.println(arr); Collections.sort(arr); System.out.println(arr); System.out.println( Collections.binarySearch(arr,20 ) ); Collections.reverse(arr); System.out.println(arr);
10. 多线程
并发 与 并行:
并发:在一段时间段 内执行多个程序
并行:在一个时间点 执行多个程序
进程 和 线程 :
进程:1个正在运行的程序【资源分配的基本单位】 线程:1个进程通常由多个线程组成(最少有1个main线程)【资源调度的基本单位】
多线程的好处 :效率高,多个线程之间互不影响
线程的调度:
分时调度:所有线程轮流使用CPU
抢占调度:让优先级
高的线程先使用CPU,如果优先级一样,则随机选一个【Java使用: 抢占调度
】
主线程:
主线程:执行 main 方法的线程
单线程程序:java默认情况只有1个线程=》main线程
JVM 的 main方法进栈 并 执行main 方法 =》产生1条进栈的路(main线程)
最常见的线程操作:
10.1 多线程的实现: 10.1.1 方式1【继承Thread类-重点】 步骤:
继承Thread类
【java.lang.Thread类】形成子类。
override 重写
Thread类的 run 方法
【线程要干什么】。
main中 new 1个 线程对象
。
线程对象.start( )
【启动线程】,JVM会自动调用 run( )来执行任务。
最终:main线程 和 新的线程 并发执行。
注意:
多次重复启动1个 线程是非法的。【尤其是在 该线程已经 执行完毕后】
java 是执行线程是 抢占式
,线程的优先级越高
,越优先执行
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package ThreadsDemo;public class TestThread extends Thread { @Override public void run () { for (int i = 0 ; i < 1000 ; i++) { System.out.println("多线程执行-" +i); } } public static void main (String[] args) { Thread th1 = new TestThread(); th1.start(); for (int i = 0 ; i <1000 ; i++) { System.out.println("main线程执行~~" ); } } }
案例1-多线程下载图片:
前提:
下载、导入jar包: commons-io.jar包
导入Jar包的步骤:
新建 lib文件夹
将 jar包 托入lib文件夹
打开 file -> project Structure -> lib -> 点击 “+” -> 应用
导入该案例所需的工具类:
import org.apache.commons.io.FileUtils;
import java.net.URL;
import java.io.File;
import java.io.IOException;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import java.io.IOException;import java.net.URL;import java.io.File;import org.apache.commons.io.FileUtils; class WebDownLoader { public void download (String url,String name) { try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常-在 download 方法中" ); } } } public class TestThread2 extends Thread { private String url; private String name; public TestThread2 (String url, String name) { this .url = url; this .name = name; } @Override public void run () { WebDownLoader wd = new WebDownLoader(); wd.download(this .url,this .name); System.out.println("下载的文件名为:" +this .name); } public static void main (String[] args) { String base_url="https://t7.baidu.com/it/u=" ; TestThread2 downTh1 = new TestThread2(base_url+"1595072465,3644073269&fm=193&f=GIF" ,"图片-1.jpg" ); TestThread2 downTh2 = new TestThread2(base_url+"825057118,3516313570&fm=193&f=GIF" ,"图片-2.jpg" ); TestThread2 downTh3 = new TestThread2(base_url+"3435942975,1552946865&fm=193&f=GIF" ,"图片-3.jpg" ); downTh1.start(); downTh2.start(); downTh3.start(); } }
10.1.2 方式2【实现Runnable接口-重点-推荐】 推荐使用
步骤:
实现Runnable接口,重写run( )方法
在main方法中,new 1个 实现类【实现类 就是多线程要抢的 资源
】
将 实现类的对象 作为参数,传入 new Thread( ) 构造方法中
线程类.start( )来自动执行run( )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package ThreadsDemo; public class TestThread3 implements Runnable { @Override public void run () { System.out.println("我是多线程" ); } public static void main (String[] args) { TestThread3 tt3 = new TestThread3(); Thread th1 = new Thread(tt3); th1.start(); } }
案例2-下载图片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package ThreadsDemo;import org.apache.commons.io.FileUtils;import java.io.File;import java.io.IOException;import java.net.URL; class WebDownLoader2 { public void download (String url,String name) { try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("download方法-> IO异常" ); } } } public class TestThread4 implements Runnable { private String url; private String name; public TestThread4 (String url, String name) { this .url = url; this .name = name; } @Override public void run () { WebDownLoader2 wd2 = new WebDownLoader2(); wd2.download(this .url,this .name); System.out.println("当前正在下载:" +this .name); } public static void main (String[] args) { String url_1 = "https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF" ; String url_2 = "https://t7.baidu.com/it/u=3908717,2002330211&fm=193&f=GIF" ; String url_3 ="https://t7.baidu.com/it/u=3785402047,1898752523&fm=193&f=GIF" ; TestThread4 tt1 = new TestThread4(url_1,"1.jpg" ); TestThread4 tt2 = new TestThread4(url_2,"2.jpg" ); TestThread4 tt3 = new TestThread4(url_3,"3.jpg" ); new Thread(tt1).start(); new Thread(tt2).start(); new Thread(tt3).start(); } }
案例3-模拟抢票: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package ThreadsDemo;public class TestThread5 implements Runnable { static int ticketNum=10 ; @Override public void run () { while (true ){ if (ticketNum<=0 )break ; System.out.println(Thread.currentThread().getName()+" => 拿到了第" +(ticketNum--)+"张票" ); try { Thread.sleep(200 ); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main (String[] args) { TestThread5 t1 = new TestThread5(); TestThread5 t2 = new TestThread5(); TestThread5 t3 = new TestThread5(); new Thread(t1,"小虹" ).start(); new Thread(t2,"小白" ).start(); new Thread(t3,"小黄" ).start(); } }
案例4-龟兔赛跑: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package ThreadsDemo;public class RaceDemo { public static void main (String[] args) { Race race = new Race(); new Thread(race,"兔子" ).start(); new Thread(race,"乌龟" ).start(); } } class Race implements Runnable { private static String winner; @Override public void run () { for (int i = 1 ; i <=100 ; i++) { if (Thread.currentThread().getName().equals("兔子" ) && i%10 ==0 ){ try { Thread.sleep(1 ); } catch (InterruptedException e) { e.printStackTrace(); } } boolean flag = isFinished(i); if (flag)break ; System.out.println(Thread.currentThread().getName()+"-> 跑了" +i+"步" ); isFinished(i); } } public boolean isFinished (int steps) { if (winner != null ){ return true ; }else { if (steps >= 100 ){ winner = Thread.currentThread().getName(); System.out.println("胜利者:" +winner); return true ; } } return false ; } }
10.1.3 方式3【实现Callable接口-了解】 好处:
步骤:
实现 Callcable接口
重写 call( )方法
创建目标对象:
创建执行服务: ExecutorService es = Executors.newFixedThreadPool(2);
提交执行: Future<Boolean> res1 = es.submit(tc1);
获取结果: boolean r1 = res1.get();
关闭服务: es.shutdownNow();
案例5:下载图片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 package ThreadsDemo;import org.apache.commons.io.FileUtils;import java.io.File;import java.io.IOException;import java.net.URL;import java.util.concurrent.*; class webImageDownLoader { public void download (String url,String name) { try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); } System.out.println("下载的文件名为 " +name); } } 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 () throws Exception { webImageDownLoader wid = new webImageDownLoader(); wid.download(this .url,this .name); return true ; } public static void main (String[] args) { TestCallable tc1 = new TestCallable("https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF" ,"图片-1.jpg" ); TestCallable tc2 = new TestCallable("https://t7.baidu.com/it/u=825057118,3516313570&fm=193&f=GIF" ,"图片-2.jpg" ); ExecutorService es = Executors.newFixedThreadPool(2 ); Future<Boolean> res1 = es.submit(tc1); Future<Boolean> res2 = es.submit(tc2); try { boolean r1 = res1.get(); boolean r2 = res1.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } es.shutdownNow(); } }
10.2 静态代理 多线程的原理 就利用了 静态代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class StaticProxy { public static void main (String[] args) { WeddingCompany weddingCompany = new WeddingCompany(new You()); weddingCompany.HappyMarry(); } } interface Marry { void HappyMarry () ; } class You implements Marry { @Override public void HappyMarry () { System.out.println("我结婚了,你哼开心~~" ); } } class WeddingCompany implements Marry { private Marry target; public WeddingCompany (Marry target) { this .target = target; } private void before () { System.out.println("布置" ); } private void after () { System.out.println("收钱" ); } @Override public void HappyMarry () { this .before(); this .target.HappyMarry(); this .after(); } }
10.3 Lambda 表达式: Lambda表达式,,是函数式编程,它可以作为匿名内部类 的替代品。
函数式接口:
一个接口,如果只有1个抽象方法
,则为 函数式接口
。【接口只有1个方法】
类似: new Thread( ()-> System.out.println("多线程的学习")).start();
格式:【函数式接口 =》需要保证:接口中只有1个抽象方法】
1 2 3 4 5 @FunctionalInterface public interface myInt { public abstract void Eat () ; }
步骤:
定义1个只有1个方法的接口
使用lambda实现方法并创建对象
调用方法
函数式接口 =》作为方法的参数 和 返回值类型
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Demo { public static void show (myInt a) { a.Eat(); } public static void main (String[] args) { this .show(new myIntImpCls()); this .show(new myIntInmp(){ }); this .show(()->{System.out.println("你好" );}); } }
案例6:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 interface Ilike { void lambda () ; } class Like implements Ilike { @Override public void lambda () { System.out.println("我喜欢lambda " ); } } public class Demo { public static void main (String[] args) { Ilike a = new Like(); a.lambda(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 interface Ilike { void lambda () ; } class Like implements Ilike { @Override public void lambda () { System.out.println("我喜欢lambda " ); } } public class Demo { public static void main (String[] args) { Ilike a = new Like(); a.lambda(); Like2 b = new Like2(); b.lambda(); class Like3 implements Ilike { @Override public void lambda () { System.out.println("我喜欢lambda " ); } } Like3 c = new Like3(); c.lambda(); Ilike d = new Ilike() { @Override public void lambda () { } }; d.lambda(); Ilike e = ( )->{ System.out.println("I like lambda" ); }; e.lambda(); } }
lambda表达式的简化: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package LandaDemo;interface Ilike { void lambda (int i) ; } public class Demo { public static void main (String[] args) { Ilike a = (int i)->{ System.out.println("lambda: " +i); System.out.println("lambda: " +i); }; a.lambda(123 ); Ilike b = (i)->{ System.out.println("lambda: " +i); System.out.println("lambda: " +i); }; b.lambda(123 ); Ilike c = i->{ System.out.println("lambda: " +i); System.out.println("lambda: " +i); }; c.lambda(123 ); Ilike d = i->System.out.println("lambda: " +i); d.lambda(123 ); } }
10.4 线程的生命周期: java.lang.Thread.State
新生:new Thread( )
就绪:线程调用 start( ) 后进入 该状态。【还没 轮到 该线程 使用cpu】
运行:线程自动调用run( )后进入 该状态
阻塞:等待
消亡:程序正常结束、出现异常、手动 调用已被废弃不用的stop( )
10.4.1 线程常用方法:
start( ):开启线程,自动调用线程中的 run( )
run( ):继承自Runnable接口的方法,必须在线程中 覆盖重写。
join( ):先start( ),再join( )。一旦 join( ),优先执行完后,才轮到别的线程执行。 【插队、阻塞】
currentThread( ):获取当前正在运行的线程。
getName( ):获取线程名
setName( 线程名 ):设置线程名
setPriority( 优先级 ):设置线程的 优先级,传入1~10,默认5。值越大,越可能被调用。
sleep( 毫秒数 ):阻塞
setDaemon( true ):将子线程 设置为 主线程的伴随线程。主线程停止时,子线程继续执行一段时间后也停止。【先设置,再start( )】
wait(毫秒数 ):阻塞,【老板等顾客】,毫秒数过后仍然没有线程调用锁对象的notify( ),则自动唤醒。
notify( ):唤醒,【包子做好给顾客】
几个方法的使用顺序:
setDaemon( true )
start( )
join( )
10.4.2 线程优先级:
优先级:1~10,默认优先级为 5
优先级 相同时,按时间片,先到先得
优先级越高,线程 被CPU先调用 的机率更高
join( )可以无视优先级,直接插队【先start( ),再join( )】
10.5 线程的同步:
多个线程抢夺到同一个资源
如:买票时,买到同一张票。
加 “锁”【同步、同步监视器】
三种方式:
10.5.1 同步代码块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class TestThread implements Runnable { @Override public void run () { for (int i = 0 ; i <10 ; i++) { synchronized (this ){ System.out.println(Thread.currentThread().getName()); } static Object obj = new Object(); synchronized (obj){ System.out.println(Thread.currentThread().getName()); } synchronized (TestThread.class){ System.out.println(Thread.currentThread().getName()); } } } }
小结:
语法:synchronized( 锁对象){ 语句 }
锁对象【同步监视器】 必须是 引用类型
,且最好使用final 修饰
。
不要 将String、包装类Interger 作为锁
在同步代码块中,不应该 改变锁对象
的引用。
可以使用一个static类型的无确切的含义的对象
来充当 锁对象
【同步监视器】
执行过程:
线程A来到同步代码块,发现“ 锁”处于open状态,于是进入,并close“锁”
线程B来到同步代码块,CPU资源切换到线程B,但B发现 “锁” close,于是阻塞。
线程A 继续接管CPU资源,执行同步代码块的内容,执行完毕后,open“锁”
小结:
同步代码块中,可以切换CPU资源,但不能执行同步代码块的内容,因为“锁”,仍处于close状态。
10.5.2 同步方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class TestThread implements Runnable { private static synchronized void sayWhoAmI () { System.out.println(Thread.currentThread().getName()); } @Override public void run () { for (int i = 0 ; i <10 ; i++) { sayWhoAmI(); } } }
10.5.3 Lock锁 import java.util.concurrent.locks.Lock;
在类的成员位置:Lock lock1 = new ReentrantLock( )
在线程问题的语句前【try-catch内】:lock1.lock( )
语句块
在线程问题的语句后【finally语句块中】:lock1.unlock( )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class TestThread implements Runnable { Lock lock1 = new ReentrantLock(); @Override public void run () { for (int i = 0 ; i <10 ; i++) { lock1.lock(); try { System.out.println(Thread.currentThread().getName()); } catch (Exception e) { e.printStackTrace(); }finally { lock1.unlock(); } } } }
小结:
Lock的优点:
Lock效率更高
可由用户控制,而之前的synchronzied由JVM控制
扩展性好,Lock是一个接口,有多个实现类
使用优先级:
Lock【推荐】 -> 同步代码块 -> 同步方法
线程安全性问题:
可能导致死锁
=》 尽可能不
使用 同步资源的嵌套
10.6 线程通信问题: 10.6.1生产者与消费者问题 经典问题:
生产者:生产商品,放入仓库
消费者:消费商品,取出仓库
以上两个线程共享资源【仓库】,但每个线程 执行的操作不同,需要线程之间的通信,来同步仓库中的商品数。
代码分析:
10.6.2 解决线程通信问题: 例子:
生产者:包子铺老板。
消费者:顾客
顾客:告诉老板 购买的包子数,顾客调用wait( )
,放弃CPU执行,进入waiting无限等待
状态
老板:花5s做包子,调用notify( )
,告知唤醒 顾客来拿包子。
注意:
老板、顾客都要使用 同步代码块
包裹。
同步代码块的锁对象
必须唯一
。
只有锁对象
才能调用 wait( )
,和notify( )
。
综上:
吃、做包子【包子和包子铺互斥,因此包子为 锁对象】
修改标志
唤醒对方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 public class Demo2 { public static void main (String[] args) { Product p = new Product(); ProcuderThread pt = new ProcuderThread(p); ClientThread ct =new ClientThread(p); new Thread(pt).start(); new Thread(ct).start(); } } class Product { private String brand; private String name; public String getBrand () { return brand; } public void setBrand (String brand) { this .brand = brand; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Product () { } public Product (String brand, String name) { this .brand = brand; this .name = name; } } class ProcuderThread implements Runnable { private Product p; public ProcuderThread (Product p) { this .p = p; } @Override public void run () { for (int i = 1 ; i <=10 ; i++) { synchronized (p){ if (i%2 ==0 ){ this .p.setBrand("德芙" ); try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } this .p.setName("巧克力" ); }else { this .p.setBrand("青岛" ); try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } this .p.setName("啤酒" ); } System.out.println("生产者生产了:" +this .p.getBrand()+"--->" +this .p.getName()); } } } } class ClientThread implements Runnable { private Product p; public ClientThread (Product p) { this .p = p; } @Override public void run () { for (int i =1 ; i <=10 ; i++) { synchronized (p){ System.out.println("消费者消费了:" +this .p.getBrand()+"--->" +this .p.getName()); } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 public class Demo2 { public static void main (String[] args) { Product p = new Product(); ProcuderThread pt = new ProcuderThread(p); ClientThread ct =new ClientThread(p); new Thread(pt).start(); new Thread(ct).start(); } } class Product { private String brand; private String name; public String getBrand () { return brand; } public void setBrand (String brand) { this .brand = brand; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Product () { } public Product (String brand, String name) { this .brand = brand; try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } this .name = name; System.out.println(); } public synchronized void setProduct (String brand, String name) { this .setBrand(brand); try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } this .setName(name); System.out.println("生产者生产了:" +this .getBrand()+"--->" +this .getName()); } public synchronized void getProduct () { System.out.println("消费者消费了:" +this .getBrand()+"--->" +this .getName()); } } class ProcuderThread implements Runnable { private Product p; public ProcuderThread (Product p) { this .p = p; } @Override public void run () { for (int i = 1 ; i <=10 ; i++) { if (i%2 ==0 ){ p.setProduct("德芙" ,"巧克力" ); }else { p.setProduct("青岛" ,"啤酒" ); } System.out.println("生产者生产了:" +this .p.getBrand()+"--->" +this .p.getName()); } } } class ClientThread implements Runnable { private Product p; public ClientThread (Product p) { this .p = p; } @Override public void run () { for (int i =1 ; i <=10 ; i++) { p.getProduct(); } } }
10.7 线程池:
第一次使用时,创建多个线程,存入集合中【集合中的线程可以复用】
使用时,取出线程
用完后,重写存入线程池
JDK1.5 之后,自带线程池,无需 用户 自己使用 集合 创建线程池。
java.util.concurrent.Executors;
=》生产 线程池 的工厂类
ExecutorService newFixedThreadPool( 线程数 );
生成线程池的方法
submit( 线程);
shudown();
线程池的好处:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package ThreadPoolDemo;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; class TestThread implements Runnable { @Override public void run () { System.out.println("这是:" +Thread.currentThread().getName()); } } public class Demo { public static void main (String[] args) { ExecutorService service = Executors.newFixedThreadPool(3 ); service.submit(new TestThread()); service.submit(new TestThread()); service.shutdownNow(); } }
11. 网络编程 整体过程:
Java中专门用于TCP通信的类:
客户端:java.net.Socket
,创建对象,发起连接
服务器:java.net.ServerSocket
,创建对象,开启服务
在TCP通信中,传输了字节流对象
, 该流对象是属于客户端
的流,服务器共用客户端的流。
Socket:
套接字,是 包含了 IP地址+端口号的网络单位。
Socket的常用方法:
构造方法:
客户端:public Socket(String host目标主机, int port目标端口);
服务器:public ServerSocket(int port);
客户端-成员方法:
getInputStream();
// 获取输入流
getOutputStream();
// 获取输出流
close()
// 关闭套接字
服务器-成员方法:
accept()
// 获取客户端的Socket
getInputStream();
// 获取输入流
getOutputStream();
// 获取输出流
close()
// 关闭套接字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 package TCPDemo; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; public class Demo2 { public static void main (String[] args) throws IOException { ServerSocket socketServer = new ServerSocket(8888 ); Socket client = socketServer.accept(); InputStream is = client.getInputStream(); byte [] buffer = new byte [1024 ]; int len=is.read(buffer); System.out.println(new String(buffer,0 ,len)); OutputStream os = client.getOutputStream(); os.write("收到,谢谢" .getBytes(StandardCharsets.UTF_8)); client.close(); socketServer.close(); } } package TCPDemo; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.nio.charset.StandardCharsets; public class Demo { public static void main (String[] args) throws IOException { Socket socketClient = new Socket("127.0.0.1" ,8888 ); OutputStream o = socketClient.getOutputStream(); o.write("你好,服务器" .getBytes(StandardCharsets.UTF_8)); InputStream is = socketClient.getInputStream(); byte [] buffer = new byte [1024 ]; int len=is.read(buffer); System.out.println(new String(buffer,0 ,len)); socketClient.close(); } }
案例1:文件上传、下载 步骤:
客户端:获取本地上传的输入流
,使用网络Socket输出流
上传文件。接收服务器的“上传成功”
服务器:获取网络Socket输入流
,使用本地下载输出流
下载文件。给客户端发送“上传成功”。
优化思路:将服务端的代码,放入Thread中,并开启多线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 package TCPDemo;import java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.nio.charset.StandardCharsets;import java.util.Random;public class FileUploadServer { public static void main (String[] args) throws Exception { ServerSocket server = new ServerSocket(8888 ); new Thread(new Runnable() { @Override public void run () { try { while (true ){ Socket client = server.accept(); InputStream is = client.getInputStream(); File file = new File("E:\\2-demo" ); if (!file.exists()){ file.mkdirs(); } byte [] buffer = new byte [1024 ]; int len = is.read(buffer); String filename = "\\txt-" +System.currentTimeMillis() +new Random().nextInt(200 ) +".txt" ; FileOutputStream fos = new FileOutputStream(file+filename); fos.write(buffer); OutputStream os = client.getOutputStream(); os.write("上传成功" .getBytes(StandardCharsets.UTF_8)); fos.close(); client.close(); } }catch (Exception e){ e.printStackTrace(); } } }).start(); } } package TCPDemo; import java.io.FileInputStream; import java.io.OutputStream; import java.net.Socket; import java.nio.charset.StandardCharsets; public class FileUploadClient { public static void main (String[] args) throws Exception { FileInputStream fis = new FileInputStream("E:\\0-demo\\1.txt" ); Socket client = new Socket("127.0.0.1" ,8888 ); OutputStream os = client.getOutputStream(); byte [] buffer = new byte [1024 ]; int len; while ((len=fis.read(buffer))!=-1 ){ os.write(buffer,0 ,len); } client.shutdownOutput(); InputStream is = client.getInputStream(); while ((len=is.read(buffer))!=-1 ){ System.out.println(new String(buffer,0 ,len)); } fis.close(); is.close(); os.close(); client.close(); } }
案例二:模拟 B/S进行通信 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 import java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.nio.charset.StandardCharsets;public class Demo { public static void main (String[] args) throws Exception { ServerSocket server = new ServerSocket(8888 ); while (true ) { Socket clientSocket = server.accept(); new Thread(new Runnable() { @Override public void run () { try { InputStream is = clientSocket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line = br.readLine(); String[] arr = line.split(" " ); String htmlpath = arr[1 ].substring(1 ); FileInputStream fis = new FileInputStream(htmlpath); OutputStream os = clientSocket.getOutputStream(); os.write("HTTP/1.1 200 OK\r\n" .getBytes()); os.write("Content-Type:text/html\r\n" .getBytes()); os.write("\r\n" .getBytes()); byte [] buff = new byte [1024 ]; int len = 0 ; while ((len = fis.read(buff)) != -1 ) { os.write(buff, 0 , len); } fis.close(); br.close(); clientSocket.close(); } catch (Exception e) { e.printStackTrace(); } } }).start(); } } }
12. Junit单元测试
Junit:
一种白盒测试工具。
12.1 Junit的使用:【@Test】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package demo.test;import org.junit.Test;import demo.junit.Calculator;public class CalcTest { @Test public void testAdd () { Calculator c = new Calculator(); int rst = c.add(1 ,2 ); Assert.assertEquals(3 ,rst); } @Test public void testSub () { Calculator c = new Calculator(); int rst = c.sub(2 ,1 ); Assert.assertEquals(1 ,2 ); } }
12.2 Junit的使用:【@Before 和 @After】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package demo.test;import org.junit.After;import org.junit.Assert;import org.junit.Before;import org.junit.Test;import demo.junit.Calculator;public class CalcTest { @Before public void init () { System.out.println("申请资源-init()" ); } @After public void close () { System.out.println("释放资源-after()" ); } @Test public void testAdd () { System.out.println("testAdd方法执行" ); Calculator c = new Calculator(); int rst = c.add(1 ,2 ); Assert.assertEquals(3 ,rst); } @Test public void testSub () { System.out.println("testSub方法执行" ); Calculator c = new Calculator(); int rst = c.sub(2 ,1 ); Assert.assertEquals(1 ,2 ); } }
13. 反射
反射 【框架设计的灵魂】:
定义:将类的组成部分,封装为各个对象。
可以在运行
的时候,获取类
的相关信息。【是Java作为准动态语言
的标志】
导包 =》new 对象 =》 得到 实例化对象
获取实例化对象 =》getClass( )来得到类信息 =》找到所在的包名
13.1 Java代码的三个阶段:
源代码阶段:编写代码【成员变量+成员方法+构造方法】
类对象阶段:通过类加载器,将代码读入 JVM【在这个阶段,源码中的1个类,被拆成3个Class对象】
运行时阶段:运行代码
反射的好处:
可以在代码运行的时候,来操作对象。【例如:IDE的代码提示功能,就是利用了反射的原理,在类对象阶段加载Methods数组】
解耦
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 import java.lang.reflect.Field; class Person { private String name; private int age; 。。。 public Person () { } public Person (String name, int age) { this .name = name; this .age = age; } } public class Demo { public static void main (String[] args) throws Exception { Class personClass = Person.class; Field[] fields = personClass.getFields(); for (Field field : fields) { System.out.println(field); } Person p = new Person("张三" ,20 ,1 ); Field a = personClass.getField("a" ); Object value = a.get(p); System.out.println(value); a.set(p,123 ); System.out.println(p); Field declaredField_name = personClass.getDeclaredField("name" ); declaredField_name.setAccessible(true ); Object val2 = declaredField_name.get(p); System.out.println(val2); Class<Person> personClass = Person.class; Constructor constructor1= personClass.getConstructor(String.class,int .class,int .class); System.out.println(constructor); Object person_1 = constructor1.newInstance("张三" ,23 ,1 ); Object person_2 = constructor1.newInstance(); Object person_3 = personClass.newInstance(); System.out.println(person_1); Class<Person> personClass = Person.class; Method eat_method = personClass.getMethod("eat" ); System.out.println(eat_method); Person p = new Person("张三" ,20 ,1 ); eat_method.invoke(p); Method eat_2 = personClass.getMethod("eat" , String.class); Person p2 = new Person("李四" ,24 ,3 ); eat_2.invoke(p2,"香蕉" ); String class_name = personClass.getName(); System.out.println(class_name); } }
13.2 案例1: 目标:
写一个工具,在不改变任何代码的前提下,可以用来:
实现思路:
实现步骤:
将需要创建的对象的全类名
和 需要调用的方法
定义在配置文件
中。
加载配置文件
。
利用反射
来加载 所需的类
到内存
中。
创建对象
执行方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Member;import java.lang.reflect.Method;import java.util.Properties;public class ReflectTest { public static void main (String[] args) throws Exception { Properties prop = new Properties(); ClassLoader classLoader_1 = ReflectTest.class.getClassLoader(); InputStream is = classLoader_1.getResourceAsStream("pro.properties" ); prop.load(is); String className_1 = prop.getProperty("className" ); String methodName_1 = prop.getProperty("methodName" ); Class<?> cls = Class.forName(className_1); Object c = cls.newInstance(); Class<?> cls2 = Class.forName(methodName_1); Method m1 = cls2.getMethod(methodName_1); m1.invoke(c); } }
13.3 获取Class对象的3种方式:
源代码阶段:Class.forName(全类名);
// 全类名:包名.类名
类加载阶段:类名.class 属性;
运行时阶段:对象名.getClass( );
// getClass( )定义在Object类中
结论:
同一个字节码文件,在程序的一次运行中,只会加载一次,不论用哪种方式获取的Class对象,最终只会是同一个。
13.4 Class对象的功能:
13.5 类加载的过程:
14. 注解(Annotation)
14.1 注解-概念:
注释
:给人
看的备注
注解
:给计算机
看的备注,也叫元数据
,JDK1.5
开始引入的特性。
14.2 注解的作用:
编写文档:生成doc文档
代码分析:使用反射
编译检查:如:Ovverride
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class AnnoDemo { public add (int a,int b) { return a+b; } }
14.2 JDK中内置的注解:
@Override
: 检查被标注的方法是否是继承自 父类/接口。
@Deprecated
: 标注的内容已过时。
@SuppressWarnings
: 压制警告 =》 @@SuppressWarnings("all")
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @SuppressWarnings("all") public class AnnoDemo { @Deprecated public add_1 (int a,int b) { return a+b; } public add_2 (int a,int b) { return a+b; } }
14.3 自定义注解:
格式:【元注解 + public @interface 注解名{ }
】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 元注解 public @interface 注解名{ } public @interface MyAnno{ } @MyAnno public void sayHello () { System.out.println("你好" ); }
注解的本质
:【继承了Annotion类的1个接口
】
1 2 3 public interface MyAnno extends java .lang .annotion ,Annotion { }
注解的属性
:【注解接口中的抽象方法
】
只要定义了注解的属性,则该注解的属性必须赋完值才能使用。
可以使用default关键字,来给注解的属性赋默认值。
如果注解只有1个属性,且该属性的名字为value,使用时,可以只传值,不写属性名。
数组在数值时,在数组的值的最外面,用 { } 包裹;数组只有1个值时,可省略大括号。
注解中抽象方法(注解的属性)的返回值的类型【5种】:
1 2 3 4 5 6 7 8 public @interface MyAnno{ int show_1 () ; String show_2 () ; MyAnno show_3 () ; String[] show_4(); }
实例:
1 2 3 4 5 6 7 8 9 10 public @interface MyAnno{ String name_method () ; int age_method () ; } @MyAnno(name_method="张三",age_method = 13) public void Eat () { System.out.println("你好" ); }
14.3.1 元注解: 概念:
元注解:修饰注解的注解。
常见的元注解:
@Target
:描述作用的位置
。如:作用在方法
ElementType:【一种枚举类型】
TYPE:作用在类
上
METHOD:作用在方法
上
FIELD:作用在成员变量
@Retention
:描述 注解 保留的阶段
。如:保留在源码阶段
RetentionPolicy:【一种枚举类型】
SOURCE:源码阶段
CLASS:class类对象阶段
RUNTIME:运行时阶段【最常用】
@Documented
:描述 注解 是否被抽取到API文档
中。
@Inheritied
:描述 注解 是否被子类继承
。
14.3.2 注解的使用:
获取 注解使用的位置的对象
利用位置对象 获取 注解对象
: obj.getAnnotion(注解名.class)
调用注解中的抽象方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface myAnnoDemo{ String name () default "ZhangSan" ; } @myAnnoDemo(name="lisi") class TestClass { public static void show () { System.out.println(); } }