一. java异常
1. 介绍
- 如何应用异常处理程序中的问题
- 如果通过包装器类实现基本数据类型的对象化处理
- 字符串处理类,String, StringBuilder 是如何进行字符串信息操作的.
- 常用集合框架及实现类的使用
- 如何使用Java输入输出流进行文件的读写
- 如何使用多线程实现数据之间的并发通信
如何处理异常
- try-catch-finally
- throw
- throws
- 自定义异常
2. 什么是异常
- 异常本质上是程序上的错误.
- 错误在我们编写程序的过程中会经常发生, 包括编译期间和运行期间的错误.
- 在程序运行过程中, 意外发生的情况,背离我们程序本身的意图的表现,都可以理解为异常.
3. 异常分类
4. 异常处理分类
- 在java应用程序中, 异常处理机制为:
- 抛出异常
- 捕捉异常
对于运行时异常, 错误或可查异常, java技术所要求的异常处理方式有所不同
- 对于可查异常必须捕捉, 或者声明抛出
- 允许忽略不可查的 RuntimeException(含子类)和Error(含子类).
通过5个关键字来实现
- try
- catch
- finally
- throw
- throws
5. try-catch-finally
实际应用中的经验与总结
- 处理运行时异常时, 采用逻辑去合理规避同时辅助 try-catch 处理
- 在多重catch 块后面, 可以加一个 catch (Exception) 来处理可能会被遗漏的异常
- 对于不确定的代码, 也可以加上 try-catch, 处理潜在的异常
- 尽量去处理异常, 切忌只是简单的调用 printStackTrace() 去打印输出
- 具体如何处理异常, 要根据不同的业务需求和异常类型去决定
- 尽量添加 finally 语句块去释放占用的资源
语法
public void method(){
try{
//代码块1
//产生异常的代码块2
}catch(异常类型 ex){
//以异常进行处理的代码段3
}finally{
//代码段4
}
}
- try 块后可以接零个或多个catch块, 如果没有catch块,则必须跟一个 finally 块.
6. 使用try-catch结构处理异常
package com.imooc.test;
import java.util.InputMismatchException;
import java.util.Scanner;
public class TryDemoOne {
public static void main(String[] args) {
// 要求: 定义2个整数,接受用户的键盘输入,输出2数之商
Scanner input = new Scanner(System.in);
try {
System.out.println("=====运算开始=====");
System.out.print("请输入第一个整数:");
int one = input.nextInt();
System.out.print("请输入第二个整数:");
int two = input.nextInt();
System.out.println("one和two的商为" + (one / two));
} catch (ArithmeticException e) {
System.out.println("除数不允许为0");
e.printStackTrace();
}catch (InputMismatchException e) {
System.exit(1); //强行终止程序的运行
System.out.println("请输入整数");
e.printStackTrace();
} catch (Exception e) {
System.out.println("出错了~~~");
e.printStackTrace();
} finally {
System.out.println("=====运算结束=====");
}
}
}
7. 使用多重catch结构处理异常
- 如上示例
8. 终止finally执行的方法
- System.exit(1); 强制中止程序运行! 参数是一个非0的参数.
9. return关键字的异常处理中的作用
- 当 try, catch, finally 中都有return的时候,最终都会返回 finally中的return数据.
- 建议 finally 里面不要写return. 因为 finally里面的代码块是强制执行的,所以 其他try, catch中return就被屏蔽了.
package com.imooc.test;
import java.util.Scanner;
public class TryDemoTwo {
public static void main(String[] args) {
int result = test();
System.out.println("one和two的商为" + result);
}
public static int test() {
Scanner input = new Scanner(System.in);
System.out.println("=====运算开始=====");
try {
System.out.print("请输入第一个整数:");
int one = input.nextInt();
System.out.print("请输入第二个整数:");
int two = input.nextInt();
return one / two;
} catch (ArithmeticException e) {
System.out.println("除数不允许为0");
return 0;
} finally {
System.out.println("=====运算结束=====");
// return -100000;
}
}
}
10. 使用throws声明异常类型
- 可以通过 throws 声明将要抛出何种类型的异常, 通过 throw 将产生的异常抛出.
- 如果一个方法可能会出现异常, 但没有能力处理这种异常, 可以在方法声明处用 throws 子句来声明抛出异常.
- throws 语句用在方法定义时声明该方法要抛出的异常类型
public void method() throws Exception1, Exception2, ..., ExceptionN{
// 可能产生异常的代码
}
- 当方法抛出异常列表中的异常时, 方法将不对这些类型及其子类类型的异常作处理, 而抛向调用该方法的方法, 由他去处理.
示例
package com.imooc.test;
import java.util.InputMismatchException;
import java.util.Scanner;
public class TryDemoThree {
public static void main(String[] args) {
try {
int result = test();
System.out.println("one和two的商为" + result);
} catch (InputMismatchException e) {
System.out.println("必须是数字");
// e.printStackTrace();
} catch (ArithmeticException e) {
System.out.println("除数不允许为0");
// e.printStackTrace();
}
}
public static int test() throws ArithmeticException, InputMismatchException {
Scanner input = new Scanner(System.in);
System.out.println("=====运算开始=====");
System.out.print("请输入第一个整数:");
int one = input.nextInt();
System.out.print("请输入第二个整数:");
int two = input.nextInt();
System.out.println("=====运算结束=====");
return one / two;
}
}
通过throws抛出异常时, 针对可能出现的多中异常情况,解决方案:
- throws 后面接多个异常类型, 中间用逗号分隔, 示例如上.
- throws 直接抛出父类 Exception.
package com.imooc.test;
import java.util.InputMismatchException;
import java.util.Scanner;
public class TryDemoThree {
public static void main(String[] args) {
try {
int result = test();
System.out.println("one和two的商为" + result);
} catch (Exception e) {
e.printStackTrace();
}
}
public static int test() throws Exception {
Scanner input = new Scanner(System.in);
System.out.println("=====运算开始=====");
System.out.print("请输入第一个整数:");
int one = input.nextInt();
System.out.print("请输入第二个整数:");
int two = input.nextInt();
System.out.println("=====运算结束=====");
return one / two;
}
}
11. 使用throw抛出异常对象
- throw 用来抛出一个异常.
例如: throw new IOException();
- throw 抛出的只能够是可抛出类 Throwable 或者其子类的实例对象.
例如: throw new String(“出错啦”); 这个是 String类型的, 所以这样写是错误的.
throw的2种写法
- 方案一, 如下格式
public void method(){
try{
//代码段1
throw new 异常类型();
}catch(异常类型 ex){
// 对异常进行处理的代码段2
}
}
- 方案二, 如下格式
public void method() throws 异常类型{
//代码段1
throw new 异常类型();
}
throw抛出异常对象的处理方案
- 通过 try…catch包含throw语句-- 自己抛出异常,自己处理
- 通过 throws 在方法声明处抛出异常类型 – 谁调动谁处理 – 调用者可以自己处理, 也可以继续上抛.
示例
package com.imooc.test;
import java.util.Scanner;
public class TryDemoFour {
public static void main(String[] args) {
try {
testAge();
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println("酒店前台工作人员不允许办理入住登记");
}
}
// 描述酒店的入住规则: 限定年龄,18岁以下,80岁以上的住客需要由亲友陪同
public static void testAge() throws Exception {
System.out.print("请输入年龄:");
Scanner input = new Scanner(System.in);
int age = input.nextInt();
if (age < 18 || age > 80) {
throw new Exception();
} else {
System.out.println("欢迎入住");
}
}
}
12. 自定义异常类
- 使用java内置的异常类可以描述在编程时出现的大部门异常情况.
- 也可以通过自定义异常描述特定业务产生的异常类型.
- 所谓自定义异常, 就是定义一个类, 去继承 Throwable 类或者它的子类.
示例
package com.imooc.test;
public class HotelAgeException extends Exception {
public HotelAgeException() {
super("18岁以下,80岁以上的住客需要由亲友陪同");
}
}
调用
throw new HotelAgeException();
完整调用例子
package com.imooc.test;
import java.util.Scanner;
public class TryDemoFour {
public static void main(String[] args) {
try {
testAge();
} catch (HotelAgeException e) {
System.out.println(e.getMessage());
System.out.println("酒店前台工作人员不允许办理入住登记");
} catch (Exception e) {
e.printStackTrace();
}
}
// 描述酒店的入住规则: 限定年龄,18岁以下,80岁以上的住客需要由亲友陪同
public static void testAge() throws HotelAgeException {
System.out.print("请输入年龄:");
Scanner input = new Scanner(System.in);
int age = input.nextInt();
if (age < 18 || age > 80) {
throw new HotelAgeException();
} else {
System.out.println("欢迎入住");
}
}
}
13. 异常链简介
- 有时候我们会捕获一个异常后在抛出另一个异常.
- 顾名思义就是: 将异常发生的原因一个传一个串起来, 即把底层的异常信息传给上层, 这样逐层抛出.
示例如下
package com.imooc.test;
public class TryDemoFive {
public static void main(String[] args) {
try {
testThree();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void testOne() throws HotelAgeException {
throw new HotelAgeException();
}
public static void testTwo() throws Exception {
try {
testOne();
} catch (HotelAgeException e) {
throw new Exception("我是新产生的异常1", e);
}
}
public static void testThree() throws Exception {
try {
testTwo();
} catch (Exception e) {
Exception e1 = new Exception("我是新产生的异常2");
e1.initCause(e);
throw e1;
// throw new Exception("我是新产生的异常2", e);
}
}
}
14. 关于方法重写时throws的注意事项
throw & throws
- 可以通过 throws 声明将要抛出何种类型的异常, 通过 throw将产生的异常抛出.
- 如果一个方法可能会出现异常, 但没有能力处理这种异常, 可以在方法声明处用 throws 字句来声明抛出异常.
throw
- 当子类重写父类抛出异常的方法时, 声明的异常必须是父类方法所声明异常的同类或子类.
二. java包装类
1. 包装类与基本数据类型
- 包装类就是为了解决 基本数据类型 没有属性,方法,无法对象化交互的问题.
- 包装类可以让 基本数据类型 拥有属性,方法, 可以对象化交互.
基本数据类型和对应的包装类
基本类型 | 对应的包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
2. 包装类常用方法
- https://docs.oracle.com/javase/8/docs/api/ 文档
3. 基本数据和包装类之间的转换
- 装箱: 把基本数据类型的值 转为 包装类的对象
- 拆箱: 把包装类的对象 转为 基本数据类型的值
包装类的获取值方法
数据类型 | 包装类方法 |
---|---|
byte | byteValue() |
int | intValue() |
double | doubleValue() |
float | floatValue() |
long | longValue() |
short | shortValue() |
示例
package wrap;
public class WrapTestDemo {
public static void main(String[] args) {
// 装箱: 把基本数据类型转换为包装类
// 1. 自动装箱
int t1 = 2;
Integer t2 = t1;
// 2. 手动装箱
Integer t3 = new Integer(t1);
// 测试
System.out.println("int类型变量t1=" + t1);
System.out.println("Integer类型对象t2=" + t2);
System.out.println("Integer类型对象t3=" + t3);
System.out.println("******************************");
// 拆箱: 把包装类转换为基本数据类型
// 1.自动拆箱
int t4 = t2;
// 2.手动拆箱
int t5 = t2.intValue();
// 测试
System.out.println("Integer类型对象t2=" + t2);
System.out.println("自动拆箱后,int类型变量t4=" + t4);
System.out.println("自动拆箱后,int类型变量t5=" + t5);
double t6 = t2.doubleValue();
System.out.println("自动拆箱后,double类型变量t6=" + t6);
}
}
4. 基本数据类型和字符串之间的转换
基本数据类型 转为 字符串
String t2 = Integer.toString(t1);
字符串 转为 基本数据类型
- 包装类的 parse 方法
int t3 = Integer.praseInt(t2);
- 包装类的 valueOf 方法,先将字符串转换为包装类, 再通过自动拆箱完成基本数据转换
int t4 = Integer.valueOf(t2);
完整示例
package wrap;
public class WrapTestTwo2 {
public static void main(String[] args) {
// 基本数据类型转换为字符串
int t1 = 2;
String t2 = Integer.toString(t1);
// 测试
System.out.println("int类型转换为String类型对象t2=" + t2);
System.out.println("***************");
// 字符串转换为基本数据类型
// 1. 包装类的 parse方法
int t3 = Integer.parseInt(t2);
// 2. 包装类的 valueOf 方法,先将字符串转换为包装类, 再通过自动拆箱完成基本数据转换
int t4 = Integer.valueOf(t2);
System.out.println("String类型转换为int类型变量t3=" + t3);
System.out.println("String类型转换为int类型变量t4=" + t4);
}
}
5. 需要知道的几点知识
- 包装类对象的初始值
- 包装类对象间的比较
package wrap;
public class Cat1Test {
public static void main(String[] args) {
// 实例化对象
Cat1 one1 = new Cat1();
// 测试输出
System.out.println("小猫的昵称:" + one1.name);
System.out.println("小猫的年龄:" + one1.month);
System.out.println("小猫的体重:" + one1.weight);
Integer one = new Integer(100);
Integer two = new Integer(100);
System.out.println("one==tow的结果" + (one == two)); //false
Integer three = 100;
// 自动拆箱
System.out.println("three=100的结果:" + (three == 100)); //true
Integer four = 100;
System.out.println("three==four的结果:" + (three == four)); //true
Integer five = 200;
System.out.println("five == 200的结果: " + (five == 200)); //true
Integer six = 200;
System.out.println("five==xie的结果:" + (five == six)); //false
}
}
三. java字符串
1. String 常用方法
创建String对象的常见3种方式
- String s1 = “imooc”; 创建一个字符串对象 imooc, 名为 s1
- String s2 = new String(); 创建一个空字符串对象, 名为 s2
- String s3 = new String(“imooc”); 创建一个字符串对象 s3, 并赋值 imooc
String 常用方法
方法 | 说明 |
---|---|
int length() | 返回当前字符串的长度 |
int indexOf(int ch) | 查找ch字符在该字符串中第一次出现的位置 |
int indexOf(String str) | 查找str子字符串在该字符串中第一次出现的位置 |
int lastIndexOf(int ch) | 查找ch字符在该字符串中最后一次出现的位置 |
int lastIndexOf(String str) | 查找str子字符串在该字符串中最后一次出现的位置 |
String substring(int beginIndex) | 获取从beginIndex位置开始到结束的子字符串 |
String substring(int beginIndex, int endIndex) | 获取从beginIndex位置开始到endIndex位置的子字符串 |
String trim() | 返回去除了前后空格的字符串 |
boolean equals(Object obj) | 将该字符串与指定对象比较, 返回true或false |
String toLowerCase() | 将字符串转换为小写 |
String toUpperCase() | 将字符串转换为大写 |
char charAt(int index) | 获取字符串中指定位置的字符 |
String[] split(String regex, int limit) | 将字符串分割为子字符串,返回字符串数组 |
byte[] getBytes() | 将该字符串转换为 byte 数组 |
举例
- length(), charAt(), substring() 方法
package com.imooc.string1;
public class StringDemo1 {
public static void main(String[] args) {
// 定义一个字符串 "JAVA 编程基础"
String str = "JAVA 编程 基础";
// 打印输出字符串的长度
System.out.println("字符串的长度是" + str.length()); //10
// 打印 '程' 字, charAt(int index)
System.out.println(str.charAt(6)); //程
// 取出字串 "编程 基础"并输出
System.out.println(str.substring(5)); //编程 基础
// 取出字串 "编程" 并输出
System.out.println(str.substring(5, 7)); //编程
}
}
- indexOf(), lastIndexOf()
package com.imooc.string1;
public class StringDemo2 {
public static void main(String[] args) {
// 定义一个字符串 "JAVA编程基础,我喜欢java编程"
String str = new String("JAVA编程基础,我喜欢java编程");
// 1.查找字符 'A'在字符串中第一次出现的位置
System.out.println("字符'A'在字符串中第一次出现的位置: " + str.indexOf('A')); // 1
// 2.查找字串"编程"在字符串中第一次出现的位置
System.out.println("字串\"编程\"在字符串中第一次出现的位置: " + str.indexOf("编程")); // 4
// 3.查找字符 'A'在字符串中第后一次出现的位置
System.out.println("字符'A'在字符串中第后一次出现的位置: " + str.lastIndexOf('A')); // 3
// 4.查找字串"编程"在字符串中第后一次出现的位置
System.out.println("字符\"编程\"在字符串中第后一次出现的位置: " + str.lastIndexOf("编程")); // 16
// 5. 在字符串index的值为8的位置开始, 查找字串 "编程" 第一次出现的位置
System.out.println("在字符串index的值为8的位置开始, 查找字串 \"编程\" 第一次出现的位置是: " + str.indexOf("编程", 8)); //16
}
}
- getBytes() 方法 将该字符串转换为 byte 数组
- String(byte[] bytes) 构造方法,可以将 byte数组转为字符串
package com.imooc.string1;
public class StringDemo3 {
public static void main(String[] args) {
// 字符串和 byte数组之间的相互转换
// 定义一个字符串
String str = new String("JAVA 编程 基础");
// 将字符串转换为 byte 数组,并打印输出
byte[] arrs = str.getBytes();
for (int i = 0; i < arrs.length; i++) {
//74 65 86 65 32 -25 -68 -106 -25 -88 -117 32 -27 -97 -70 -25 -95 -128
System.out.print(arrs[i] + " ");
}
System.out.println();
// 将byte数组转换为字符串
String str1 = new String(arrs);
System.out.println(str1); //JAVA 编程 基础
}
}
2. 等于运算符与equals方法的区别
- “==” 指的是 2个对象的引用是否相同
- equals() 方法指的是 2个对象的值 是否相同
例子如下
package com.imooc.string1;
public class StringDemo5 {
public static void main(String[] args) {
// == 和 equals 方法之间的区别
// 定义三个字符串,内容都是 imooc
String str1 = "imooc";
String str2 = "imooc";
String str3 = new String("imooc");
System.out.println("str1和str2的内容相同?" + str1.equals(str2)); //true
System.out.println("str1和str3的内容相同?" + str1.equals(str3)); //true
System.out.println("str1和str2的地址相同?" + (str1 == str2)); //true
System.out.println("str1和str3的地址相同?" + (str1 == str3)); //false
}
}
3. 字符串的不可变性
- String 对象一旦被创建,则不能修改,是不可变的
- 所谓的修改其实是创建了新的对象, 所指向的内容空间不变
例子
package com.imooc.string1;
public class StringDemo6 {
public static void main(String[] args) {
// String的不可变性
// String 对象一旦被创建,则不能修改,是不可变的
// 所谓的修改其实是创建了新的对象, 所指向的内容空间不变
String s1 = "imooc";
String s2 = "hello, " + s1;
// s1不再指向imooc所在的内存空间,而是指向了 "hello,imooc"
System.out.println("s1=" + s1); // imooc
System.out.println("s2=" + s2); // hello, imooc
String s3 = new String("hello,imooc!");
System.out.println("字串:" + s3.substring(0, 5));
System.out.println("s3=" + s3);
}
}
4. StringBuilder 概述
String和StringBuilder的区别
- String 具有不可变性, 而 StringBuilder不具备
建议
- 当频繁操作字符串时,使用 StringBuilder.
StringBuilder和StringBuffer
- 二者基本相似
- StringBuffer 是线程安全的, StringBuilder则没用, 所以性能略高.
5. StringBuilder常用方法
- StringBuilder append(String str) 添加方法
- StringBuilder delete(int start, int end) 删除方法
- StringBuilder insert(int offset, String str) 插入方法
- StringBuilder replace(int start, int end, String str) 替换方法
package com.imooc.string1;
public class StringBuilderDemo1 {
public static void main(String[] args) {
// 定义一个字符串 "你好"
StringBuilder str = new StringBuilder("你好");
// 在"你好"后面添加内容,将字符串变成 "你好,imooc"
/*
* 写法一
*
* str.append(','); str.append("imooc"); System.out.println("str=" + str);
*/
/*
* 写法二
*/
System.out.println("str=" + str.append(',').append("imooc")); // str=你好,imooc
// 将字符串变成 "你好,IMOOC"
// 两种方式
/*
* 1. 使用delete方法删除imooc, 然后再插入 IMOOC
*
* System.out.println("替换后: " + str.delete(3, 8).insert(3, "IMOOC"));
*/
// 2. 使用 replace 方法直接替换
System.out.println("替换后: " + str.replace(3, 8, "IMOOC")); // 替换后: 你好,IMOOC
// 3. 在字符串 "你好,IMOOC" 中取出 "你好" 并输出
System.out.println(str.substring(0, 2)); // 你好
}
}
四. java集合
1. 集合概述
-
概念
-
体系结构
-
实际应用
-
java 中的集合是工具类,可以存储任意数量的具有共同属性的对象.
应用场景
- 无法预测存储数据的数量
- 同时存储具有一对一关系的数据
- 需要进行数据的增删
- 数据重复问题
2. 集合框架的体系结构
- Collection 存放对象
- Map 存放键值对
3. list(列表)概述
- List是元素有序并且可以重复的集合, 称为序列.
- List 可以精确的控制每个元素的插入位置, 或删除某个位置的元素
- List 的两个主要实现类是 ArrayList 和 LinkedList
ArrayList
- ArrayList 底层是由数组实现的
- 动态增长, 以满足应用程序的需要
- 在列表尾部插入或删除数据非常有效
- 更适合查找和更新元素
- ArrayList 中的元素可以为 null
4. 案例:在list中存储并操作字符串信息
ArrayList() 的常见方法
- add() 添加元素
- size() 查看 ArrayList() 中元素的个数
- get(Int index) 根据下标查看元素
- remove(Int index) / remove(Object o) 移出元素,可以接下标或者字符串的值.
例子
package com.imooc.set1;
import java.util.ArrayList;
import java.util.List;
public class ListDemo1 {
public static void main(String[] args) {
// 用ArrayList存储编程语言的名称, 并输出
List list = new ArrayList();
// 添加内容
list.add("JAVA");
list.add("C");
list.add("C++");
list.add("Go");
list.add("swift");
// 输出列表中元素的个数
System.out.println("列表中元素的个数为: " + list.size()); // 列表中元素的个数为: 5
System.out.println("**********************");
// 遍历输出所有的编程语言
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " "); // JAVA C C++ Go swift
}
System.out.println("");
/**
* 移出列表中的 C++ 方法一
*
* list.remove(2); // 遍历输出所有的编程语言
* for (int i = 0; i < list.size(); i++) {
* System.out.print(list.get(i) + " "); //JAVA C Go swift
* }
*/
System.out.println("");
/**
* 移出列表中的 C++ 方法二
*/
list.remove("C++");
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " "); // JAVA C Go swift
}
}
}
5. 公告需求管理
- 在 ArrayList() 中添加 自定义类 的对象.
公告需求管理 需求
- 公告的添加和显示
- 在指定位置处插入广告
- 删除公告
- 修改公告
公告类属性
- 编号 id
- 标题 title
- 创建人 creator
- 创建时间 createTime
公告类方法
- 构造方法
- 获取和设置属性值的方法
6. 案例:公告类添加和实现
需求
- 公告的添加和显示
- 在指定位置处出入广告
- 删除公告
公告类
package com.imooc.set1;
import java.util.Date;
public class Notice {
private int id; // ID
private String title; // 标题
private String creator; // 创建人
private Date createTime; // 创建时间
public Notice() {
}
public Notice(int id, String title, String creator, Date createTime) {
this.setId(id);
this.setTitle(title);
this.setCreator(creator);
this.setCreateTime(createTime);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
公告测试类
package com.imooc.set1;
import java.util.ArrayList;
import java.util.Date;
public class NoticeTest {
public static void main(String[] args) {
// 创建Notice类的对象, 生成三条公告
Notice notice1 = new Notice(1, "欢迎来到mooc", "管理员", (new Date()));
Notice notice2 = new Notice(2, "请同学们按时提交作业", "老师", (new Date()));
Notice notice3 = new Notice(3, "考勤通知", "老师", (new Date()));
// 添加公告
ArrayList noticeList = new ArrayList();
noticeList.add(notice1);
noticeList.add(notice2);
noticeList.add(notice3);
// 显示公告
for (int i = 0; i < noticeList.size(); i++) {
System.out.println(i + 1 + ": " + ((Notice) (noticeList.get(i))).getTitle() + " ");
}
/**
* 输出
* 1: 欢迎来到mooc
* 2: 请同学们按时提交作业
* 3: 考勤通知
*
*/
}
}
PS: ((Notice) (noticeList.get(i))).getTitle() 这里的解释:
- noticeList.get(i) 获取到的是 Object 对象.
- (Notice) (noticeList.get(i))) 将 Object对象转为 Notice 对象.
- ((Notice) (noticeList.get(i))).getTitle() 调用 Notice 对象的getTitle() 来获取标题. 注意括号.
7. 案例:公告的删除和修改
在指定位置添加公告 - add() 方法
// 在第一条公告后面添加一条新的公告 add() 方法
Notice notice4 = new Notice(4, "在线编辑器可以使用了!", "管理员", (new Date()));
noticeList.add(1, notice4);
删除公告 - remove() 方法
noticeList.remove(2);
修改公告 - set() 方法
Notice notice4 = new Notice(4, "在线编辑器可以使用了!", "管理员", (new Date()));
...
notice4.setTitle("Java在线编辑器可以使用啦!");
noticeList.set(1, notice4);
完整代码
package com.imooc.set1;
import java.util.ArrayList;
import java.util.Date;
public class NoticeTest {
public static void main(String[] args) {
// 创建Notice类的对象, 生成三条公告
Notice notice1 = new Notice(1, "欢迎来到mooc", "管理员", (new Date()));
Notice notice2 = new Notice(2, "请同学们按时提交作业", "老师", (new Date()));
Notice notice3 = new Notice(3, "考勤通知", "老师", (new Date()));
// 添加公告
ArrayList noticeList = new ArrayList();
noticeList.add(notice1);
noticeList.add(notice2);
noticeList.add(notice3);
// 在第一条公告后面添加一条新的公告
Notice notice4 = new Notice(4, "在线编辑器可以使用了!", "管理员", (new Date()));
noticeList.add(1, notice4);
// 显示公告
for (int i = 0; i < noticeList.size(); i++) {
System.out.print(i + 1 + ": " + ((Notice) (noticeList.get(i))).getTitle() + " ");
}
// 输出: 1: 欢迎来到mooc 2: 在线编辑器可以使用了! 3: 请同学们按时提交作业 4: 考勤通知
System.out.println("**************");
// 删除按时完成作业的公告
noticeList.remove(2);
// 显示公告
for (int i = 0; i < noticeList.size(); i++) {
System.out.print(i + 1 + ": " + ((Notice) (noticeList.get(i))).getTitle() + " ");
}
// 输出: 1: 欢迎来到mooc 2: 在线编辑器可以使用了! 3: 考勤通知
// 修改公告, 将第二条公告改为: Java在线编辑器可以使用啦!
System.out.println("**************");
// 先修改第二条公告中 title 的值
notice4.setTitle("Java在线编辑器可以使用啦!");
noticeList.set(1, notice4);
// 显示公告
for (int i = 0; i < noticeList.size(); i++) {
System.out.print(i + 1 + ": " + ((Notice) (noticeList.get(i))).getTitle() + " ");
}
//输出: 欢迎来到mooc 2: Java在线编辑器可以使用啦! 3: 考勤通知
}
}
8. set概述
- Set是元素无序并且不可以重复的集合, 被称为 集.
- HashSet 是 Set 的一个重要实现类, 称为 哈希集.
- HashSet 中的元素无序并且不可以重复.
- HashSet 中只允许一个 null 元素.
- 具有良好的存取和查找性能.
9. Iterator(迭代器)
Iterator接口以统一的方式对各种集合元素进行遍历
Iterator<String> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next() + " ");
}
- Iterator 接口 可以以统一的方式对各种集合元素进行遍历
- hasNext() 方法检测集合中是否还有下一个元素
- next() 方法返回集合中的下一个元素
迭代器的使用示例
package com.imooc.set1;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class WordDemo {
public static void main(String[] args) {
// 将英文单词添加到 HashSet 中
Set set = new HashSet();
// 向集合中添加元素
set.add("blue");
set.add("red");
set.add("black");
set.add("yellow");
set.add("white");
// 显示集合的内容
System.out.println("集合中的元素为:");
// 使用迭代器, 把数据放到 迭代器中
Iterator it = set.iterator();
// 遍历迭代器并输出元素
while (it.hasNext()) {
System.out.print(it.next() + " "); // 输出: red blue white black yellow
}
}
}
- Set 中是不允许有重复数据的,如果插入相同数据了, eclipse中不会提示错误,但是会插入失败.
- Set 中的数据是无序不允许重复的,所以和列表不同,不存在在一个位置插入数据.
10. HashSet中添加自定义类的对象
案例: 宠物猫信息管理
需求
- 添加和显示宠物猫信息
- 查找某只宠物猫的信息并输出
- 修改宠物猫的信息
- 删除宠物猫的信息
属性
- 名字 name
- 年龄 month
- 品种 species
方法
- 构造方法
- 获取和设置属性值的方法
- 其他方法
11. 添加和显示信息
- HashSet 添加信息 add()
- HashSet 显示信息,用 迭代器实现.
具体实现
- Cat 类
package com.imooc.set1;
public class Cat {
private String name; // 名字
private int month; // 年龄
private String species; // 品种
// 构造方法
public Cat() {
}
public Cat(String name, int month, String species) {
this.setName(name);
this.setMonth(month);
this.setSpecies(species);
}
// getter和setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
@Override
public String toString() {
return " [姓名: " + name + ", 年龄: " + month + ", 品种: " + species + "]";
}
}
- 测试类
package com.imooc.set1;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class CatTest {
public static void main(String[] args) {
// 定义宠物猫类
Cat huahua = new Cat("花花", 12, "英国短毛猫");
Cat fanfan = new Cat("凡凡", 3, "中华田园猫");
// 将宠物猫对象放入HashSet中
Set set = new HashSet();
set.add(huahua);
set.add(fanfan);
// 显示宠物猫信息
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
插入重复数据
- 注意: 这里插入重复的自定义类对象的时候, 需要重写 hashCode() 和 equals() 方法. 代码如下
package com.imooc.set1;
public class Cat {
private String name; // 名字
private int month; // 年龄
private String species; // 品种
// 构造方法
public Cat() {
}
public Cat(String name, int month, String species) {
this.setName(name);
this.setMonth(month);
this.setSpecies(species);
}
// getter和setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
@Override
public String toString() {
return " [姓名: " + name + ", 年龄: " + month + ", 品种: " + species + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + month;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((species == null) ? 0 : species.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
// 判断对象 是否相等, 相等则返回true, 不用继续比较属性了
if (this == obj) {
return true;
}
// 判断obj是否是Cat类的对象
if (obj.getClass() == Cat.class) {
Cat cat = (Cat) obj;
return (cat.getName().equals(name)) && (cat.getMonth() == month) && (cat.getSpecies().equals(species));
}
return false;
}
}
12. 查找宠物猫信息
...
// 查找一: 在集合中查找花花的信息并输出
if (set.contains(huahua)) {
System.out.println("花花找到了!");
System.out.println(huahua);
} else {
System.out.println("花花没找到!");
}
// 查找二: 在集合中根据名字查找花花的信息
boolean flag = false;
Cat c = null;
it = set.iterator();
while (it.hasNext()) {
c = (Cat) it.next();
if (c.getName().equals("花花")) {
flag = true; // 找到了
break;
}
}
if (flag) {
System.out.println("花花找到了");
System.out.println(c);
}else {
System.out.println("花花没找到");
}
...
13. 删除元素
泛型
- jdk5.0 之后出来的,表示 后面的类型指定为某一个特定类型
例如 Set<Cat> set = new HashSet<Cat>(); 类名后面的 <cat> 和构造方法后面的<cat> 都需要加.
删除代码如下
// 增强型for循环
// 删除 花花二代的信息并重新输出
for (Cat cat : set) {
if (cat.getName().equals("花花二代")) {
set.remove(cat);
break;
}
}
System.out.println("删除花花二代后的数据");
// 输出元素
for (Cat cat : set) {
System.out.println(cat);
}
// 删除集合中的所有宠物猫信息
System.out.println("************");
boolean flag1 = set.removeAll(set);
if (flag1) {
System.out.println("删除成功!");
} else {
System.out.println("猫还在!");
}
// 上面的 if(flag1) 也可以换成 if(set.isEmpty())
14. Map概述
- Map 中的数据是以键值对(key-value) 的形式存储的
- key-value 以 Entry 类型的对象实例存在
- 可以通过key值快速的查找value
- 一个映射不能包含重复的键
- 每个键最多只能映射一个值
HashMap
- 基于哈希表的Map接口的实现
- 允许使用null值和null键
- key值不允许重复
- HashMap 中的 Entry对象是无序排列的
15. 案例:在字典中添加内容并显示
完成一个类似字典的功能
- 将单词以及单词的注释存储到 HashMap 中
- 显示 HashMap 中的内容
- 查找某个单词的注释并显示
HashMap中常见方法
- put(k,v) 添加数据
HashMap 添加数据,显示数据,查找数据 案例如下
package com.imooc.set1;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.Set;
public class DictionaryDemo {
public static void main(String[] args) {
// 定义一个 HashMap的对象,来存储 字典的内容
Map<String, String> animal = new HashMap<String, String>();
System.out.println("请输入三组单词对应的注释, 并存放到HashMap中");
Scanner console = new Scanner(System.in);
// 添加数据
int i = 0;
while (i < 3) {
System.out.println("请输入key的值(单词): ");
String key = console.next();
System.out.println("请输入value值(注释): ");
String value = console.next();
animal.put(key, value);
i++;
}
System.out.println("*******************");
// 打印输出value的值(直接使用迭代器完成)
System.out.println("使用迭代器输出所有的value:");
Iterator<String> it = animal.values().iterator(); // animal.values()返回的是一个集合. 再调用集合的iterator()迭代器方法
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
System.out.println();
System.out.println("*******************");
// 打印输出key和value的值, 通过 entrySet方法
System.out.println("通过entrySet方法得到key-value");
Set<Entry<String, String>> entrySet = animal.entrySet();
for (Entry<String, String> entry : entrySet) {
System.out.print(entry.getKey() + "-");
System.out.print(entry.getValue() + " ");
}
System.out.println();
System.out.println("*******************");
// 通过单词找到注释并输出, 使用 keySet方法
String strSearch = console.next();
// 1. 取得keySet()
Set<String> keySet = animal.keySet();
// 2. 遍历keySet()
for (String key : keySet) {
if (strSearch.equals(key)) {
System.out.println("找到了!" + "键值对为: " + key + "-" + animal.get(key));
break;
}
}
}
}
16. 商品信息管理
1. 商品信息管理需求
- 使用 HashSet 对商品信息进行管理,其中 key为商品编号, value为商品对象.
- 对HashMap中的商品信息进行增,删,改,查操作.
2. 分析商品信息类
2.1 属性
- 商品编号: id
- 商品名称: name
- 商品价格: price
2.2 方法
- 构造方法
- 获取和设置属性值的方法
- 其他方法
3. 代码实现, 先建Goods类,如下
package com.imooc.set1;
public class Goods {
private int id; // 商品编号
private String name; // 商品名称
private int price; // 商品价格
public Goods() {
}
public Goods(int id, String name, int price) {
this.setId(id);
this.setName(name);
this.setPrice(price);
}
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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "商品编号: " + id + ", 商品名称: " + name + ",商品价格: " + price;
}
}
17. 商品信息添加
如上,添加 GoodTest 类,测试添加信息, 显示信息
package com.imooc.set1;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
public class GoodsTest {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
// 定义HashMap对象
Map<String, Goods> goodsMap = new HashMap<String, Goods>();
System.out.println("请输入三条商品信息: ");
int i = 0;
while (i < 3) {
System.out.println("请输入" + (i + 1) + "条商品信息:");
System.out.println("请输入商品编号: ");
String goodsId = console.next();
System.out.println("请输入商品名称: ");
String goodsName = console.next();
System.out.println("请输入商品价格: ");
double goodsPrice = console.nextDouble();
Goods goods = new Goods(goodsId, goodsName, goodsPrice);
// 将商品信息添加到 HashMap中
goodsMap.put(goodsId, goods);
i++;
}
// 遍历Map, 输出商品信息
System.out.println("商品的全部信息为:");
Iterator<Goods> itGoods = goodsMap.values().iterator();
while (itGoods.hasNext()) {
System.out.println(itGoods.next());
}
}
}
18. 商品信息添加优化
上述代码中,还有几点不完善
- 编号不能重复
String goodsId = console.next();
// 判断商品编号id是否存在
if(goodsMap.containsKey(goodsId)) {
System.out.println("该商品编号已经存在! 请重新输入! ");
continue;
}
- 输入商品价格的时候,如果输入字符的话,会报错.
double goodsPrice = 0;
try {
goodsPrice = console.nextDouble();
} catch (java.util.InputMismatchException e) {
System.out.println("商品价格的格式不正确,请输入数值型数据!");
console.next(); //这里需要注意,需要加一个
continue;
}
完整代码
package com.imooc.set1;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
public class GoodsTest {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
// 定义HashMap对象
Map<String, Goods> goodsMap = new HashMap<String, Goods>();
System.out.println("请输入三条商品信息: ");
int i = 0;
while (i < 3) {
System.out.println("请输入" + (i + 1) + "条商品信息:");
System.out.println("请输入商品编号: ");
String goodsId = console.next();
// 判断商品编号id是否存在
if (goodsMap.containsKey(goodsId)) {
System.out.println("该商品编号已经存在! 请重新输入! ");
continue;
}
System.out.println("请输入商品名称: ");
String goodsName = console.next();
System.out.println("请输入商品价格: ");
double goodsPrice = 0;
try {
goodsPrice = console.nextDouble();
} catch (java.util.InputMismatchException e) {
System.out.println("商品价格的格式不正确,请输入数值型数据!");
console.next(); //这里需要注意,需要加一个
continue;
}
Goods goods = new Goods(goodsId, goodsName, goodsPrice);
// 将商品信息添加到 HashMap中
goodsMap.put(goodsId, goods);
i++;
}
// 遍历Map, 输出商品信息
System.out.println("商品的全部信息为:");
Iterator<Goods> itGoods = goodsMap.values().iterator();
while (itGoods.hasNext()) {
System.out.println(itGoods.next());
}
}
}
五. java线程
1. 什么是线程
进程的概念
- 进程是指可执行程序并存放在计算机存储器的一个指令序列, 它是一个动态执行的过程.
什么是线程
- 线程是比进程还要小的运行单位,一个进程包含多个线程, 线程可以看出一个子程序.
2. Thread和Runnable接口介绍
线程的创建,有2种方式
- 创建一个 Thread 类,或者一个 Thread 子类的对象
- 创建一个实现 Runnable 接口的类的对象
- Thread 是一个线程类, 位于 java.lang 包下
构造方法 | 说明 |
---|---|
Thread() | 创建一个线程对象 |
Thread(String name) | 创建一个具有指定名称的线程对象 |
Thread(Runnable target) | 创建一个基于Runnable接口实现类的线程对象 |
Thread(Runnable target,String name) | 创建一个基于Runnable接口实现类,并且具有指定名称的线程对象. |
- Thread类 的常用方法
方法 | 说明 |
---|---|
public void run() | 线程相关的代码写在该方法中,一般需要重写 |
public void start() | 启动线程的方法 |
public static void sleep(long m) | 线程休眠m毫秒的方法 |
public void join() | 优先执行调用join()方法的线程 |
Runnable 接口
- 只有一个方法 run();
- Runnable 是 Java 中用以实现线程的接口
- 任何实现线程功能的类都必须实现该接口
3. 通过Thread类创建线程
- 通过继承 Thread类的方式创建线程类, 重写 run() 方法
案例
- 新建一个 MyThread 类继承自 Thread
package com.imooc.thread;
public class MyThread extends Thread {
public void run() {
System.out.println(getName() + "该线程正在执行!");
}
}
- 新建一个测试类
package com.imooc.thread;
public class ThreadTest {
public static void main(String[] args) {
System.out.println("主线程1");
MyThread mt = new MyThread();
mt.start(); // 启动线程, 只能启动一次
System.out.println("主线程2");
/**
* 输出
* 主线程1
* 主线程2
* Thread-0该线程正在执行!
*
*/
}
}
4. 实现Runnable接口创建线程
- 通过实现Runnable接口的方式创建.
为什么要实现Runnable接口
- Java不支持多继承
- 不打算重写 Thread 类的其他方法
实现Runnable接口创建线程步骤有三
- 创建 Runnable类的实现对象
- 创建 Thread类的对象
- 启动线程
代码如下
package com.imooc.runnable;
class PrintRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在运行!");
}
}
public class Test {
public static void main(String[] args) {
// 1 创建 Runnable类的实现对象
PrintRunnable pr1 = new PrintRunnable();
// 2 创建 Thread类的对象
Thread t1 = new Thread(pr1);
// 3 启动线程
t1.start();
// 创建 Runnable类的实现对象
PrintRunnable pr2 = new PrintRunnable();
// 创建 Thread类的对象
Thread t2 = new Thread(pr2);
// 启动线程
t2.start();
}
}
5. 线程的状态和生命周期
线程状态
- 新建(New)
- 可运行(Runnable) / 也叫 就绪状态
- 正在运行(Running)
- 阻塞(Blocked)
- 终止(Dead)
线程的生命周期
6. sleep方法的使用
- sleep方法是 Thread类的方法
public static void sleep(long millis)
- sleep方法的作用: 在指定的毫秒数内让正在执行的线程休眠(暂停执行)
- 参数为休眠的时间,单位是毫秒
package com.imooc.sleep;
class MyThread implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 30; i++) {
System.out.println(Thread.currentThread().getName() + "执行第" + i + "次!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class SleepDemo {
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread t = new Thread(mt);
t.start();
MyThread mt1 = new MyThread();
Thread t1 = new Thread(mt1);
t1.start();
}
}
7. join方法的使用
- join方法 也是 Thread类的方法
public final void join()
- join方法的作用: 等待调用该方法的线程结束后才能执行 (调用了join()方法的线程优先执行,执行完才轮到其他线程执行.)
public final void join(long millis)
- 上述作用: 等待该线程终止的最长时间为 millis 毫秒.
例子如下
package com.imooc.join;
class MyThread extends Thread {
public void run() {
System.out.println(getName() + "正在执行!");
}
}
public class JoinDemo {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
try {
mt.join(); // 抢占资源,优先执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程运行结束!");
}
/**
* 输出 Thread-0正在执行!
* 主线程运行结束!
*
*/
}
8. 线程的优先级
-
Java 为线程提供了 10个优先级,数字越大,优先级越高.
-
优先级可以用整数 1-10 表示,超出范围会抛出异常.
-
主线程默认优先级为5.
-
除了使用 数字来表示线程的优先级,还可以用 优先级常量来表示.
-
MAX_PRIORITY: 线程的最高优先级 10
-
MIN_PRIORITY: 线程的最低优先级 1
-
NORM_PRIORITY: 线程的默认优先级 5
优先级相关的方法
方法 | 说明 |
---|---|
public int getPriority() | 获取线程优先级的方法 |
public void setPriority(int newPriority) | 设置线程优先级的方法 |
案例
package com.imooc.priority;
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i <= 50; i++) {
System.out.println("线程" + name + "正在运行" + i);
}
}
}
public class Priority {
public static void main(String[] args) {
// 获取主线程的优先级
// Thread.currentThread() 表示当前线程
int mainPriority = Thread.currentThread().getPriority();
System.out.println("主线程的优先级为: " + mainPriority);
// 自定义线程
MyThread mt1 = new MyThread("线程一");
// mt1.setPriority(10);
mt1.setPriority(Thread.MAX_PRIORITY);
mt1.start();
MyThread mt2 = new MyThread("线程二");
mt2.setPriority(Thread.MIN_PRIORITY);
mt2.start();
System.out.println("线程一的优先级为: " + mt1.getPriority());
System.out.println("线程二的优先级为: " + mt2.getPriority());
}
}
9. 线程同步
多线程运行问题
- 各个线程是通过竞争cpu时间而获得运行机会的.
- 各线程什么时候得到CPU时间,占用多久,是不可预测的.
- 一个正在运行着的线程在什么地方被暂停是不确定的.
银行存取款问题
- 为了保证在存款或取款的时候, 不允许其他线程对账户余额进行操作.
- 需要将 Bank 对象进行锁定.
- 使用同步关键字 synchronized 实现 对 Bank 对象的锁定.
synchronized关键字用在
- 成员方法
- 静态方法
- 语句块
public synchronized void saveAccount(){…}
public static synchronized void saveAccount() {…}
synchronized (obj){…}
案例源码
- Bank 类
package com.imooc.bank;
public class Bank {
private String account; // 账号
private int balance; // 余额
public Bank() {
}
public Bank(String account, int balance) {
this.setAccount(account);
this.setBalance(balance);
}
public String getAccount() {
return this.account;
}
public void setAccount(String account) {
this.account = account;
}
public int getBalance() {
return this.balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Bank [账号: " + this.account + ",余额: " + balance + "]";
}
// 存款
public synchronized void saveAccount() {
// 可以在不同的位置处添加 sleep 方法
// 获取当前的账户余额
int balance = this.getBalance();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 修改余额, 寸100元
balance += 100;
// 修改账户余额
this.setBalance(balance);
// 输出存款后的账户余额
System.out.println("存款后的账户余额为: " + balance);
}
// 取款
public void drawAccount() {
// synchronized的另外一种使用方法
synchronized (this) {
// 在不同的位置处添加sleep方法
// 获取当前的账户余额
int balance = getBalance();
// 修改余额, 取200
balance = balance - 200;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 修改账户余额
this.setBalance(balance);
System.out.println("取款后的账户余额: " + balance);
}
}
}
- 存款类 SaveAccount
package com.imooc.bank;
public class SaveAccount implements Runnable {
Bank bank;
public SaveAccount(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
bank.saveAccount();
}
}
- 取款类 DrawAccount
package com.imooc.bank;
public class DrawAccount implements Runnable {
Bank bank;
public DrawAccount(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
bank.drawAccount();
}
}
- 代码测试类 Test
package com.imooc.bank;
public class Test {
public static void main(String[] args) {
// 创建账户,给定余额1000
Bank bank = new Bank("1001", 1000);
// 创建线程对象
SaveAccount sa = new SaveAccount(bank);
DrawAccount da = new DrawAccount(bank);
Thread save = new Thread(sa);
Thread draw = new Thread(da);
save.start();
draw.start();
try {
draw.join();
save.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(bank);
}
/**
* 输出 存款后的账户余额为: 1100
* 取款后的账户余额: 900
* Bank [账号: 1001,余额: 900]
*
*/
}
10. 线程间通信
- 如上述 银行存取款的案例, 那么 如果账户余额不够了怎么办?
- 等待存入足够的钱后处理.
线程间通信
- wait() 方法: 中断方法的执行, 使线程等待.
- notify() 方法: 唤醒处于等待的某一个线程, 使其结束等待.
- notifyAll() 方法: 唤醒所有处于等待的线程, 使它们结束等待.
案例
- Queue类
package com.imooc.queue;
public class Queue {
private int n;
boolean flag = false;
public Queue() {
}
public synchronized int getN() {
if (!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费: " + this.n);
flag = false; // 消费完毕,容器中没有数据
notifyAll();
return n;
}
public synchronized void setN(int n) {
if (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("生产: " + n);
this.n = n;
flag = true; // 生成完毕, 容器中已经有数据
notifyAll();
}
}
- 生产者类 Producer
package com.imooc.queue;
public class Producer implements Runnable {
Queue queue;
public Producer() {
}
public Producer(Queue queue) {
this.queue = queue;
}
@Override
public void run() {
int i = 0;
while (true) {
queue.setN(i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 消费者类
package com.imooc.queue;
public class Consumer implements Runnable {
Queue queue;
public Consumer() {
}
public Consumer(Queue queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
queue.getN();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 测试类 Test
package com.imooc.queue;
public class Test {
public static void main(String[] args) {
Queue queue = new Queue();
new Thread(new Producer(queue)).start();
new Thread(new Consumer(queue)).start();
}
}
六. java输入流
1. 概述
-
输出流 write(写操作)
-
输入流 read(读操作)
2. File类
什么是文件?
- 文件可认为是相关记录或放在一起的数据的集合.
- 在 Java 中,使用 java.io.File 类对文件进行操作.
3. File类的常用方法
- 创建 File 对象的几种方法
- 方式一, 直接写路径
File file1 = new File("/Users/zhangqiang/Desktop/demo/io/score.txt");
注意: 环境是 mac, 如果是windows系统 路径应该是 new File(“c:\\imooc\\set\\HashSet”)
- 方式二, 把路径拆分写
File file1 = new File("/Users/zhangqiang/Desktop/demo", "io/score.txt");
- 方式三, 父路径作为第一个对象,然后在父对象上,再申明一个对象
File file = new File("/Users/zhangqiang/Desktop/");
File file1 = new File(file, "demo/io/score.txt");
- 判断是文件还是目录
// 判断是文件还是目录
System.out.println("是否是目录: " + file1.isDirectory()); // false
System.out.println("是否是文件: " + file1.isFile()); // true
- 创建目录 mkdir()创建单级目录, mkdirs()创建多级目录
//创建目录, 单级目录 mkdir()
File file2 = new File("/Users/zhangqiang/Desktop/demo/set/", "HashSet");
if(!file2.exists()) {
file2.mkdir();
}
//创建目录, 多级目录 mkdirs()
File file3 = new File("/Users/zhangqiang/Desktop/demo/set/", "HashSet/test");
if(!file3.exists()) {
file3.mkdirs();
}
- 创建文件 createNewFile()
//创建文件
if(!file1.exists()) {
try {
file1.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
完整示例
package com.imooc.file;
import java.io.File;
import java.io.IOException;
public class FileDemo {
public static void main(String[] args) {
/**
* 创建一个 File 对象, 方式一, 直接写路径
* File file1 = new File("/Users/zhangqiang/Desktop/demo/io/score.txt");
*/
/**
* 创建一个 File 对象, 方式二, 把路径拆分写
* File file1 = new File("/Users/zhangqiang/Desktop/demo", "io/score.txt");
*
*/
/**
* 创建一个 File 对象, 方式三, 父路径作为第一个对象,然后在父对象上,再申明一个对象
*
*/
File file = new File("/Users/zhangqiang/Desktop/");
File file1 = new File(file, "demo/io/score.txt");
// 判断是文件还是目录
System.out.println("是否是目录: " + file1.isDirectory()); // false
System.out.println("是否是文件: " + file1.isFile()); // true
//创建目录, 单级目录 mkdir()
File file2 = new File("/Users/zhangqiang/Desktop/demo/set/", "HashSet");
if(!file2.exists()) {
file2.mkdir();
}
//创建目录, 多级目录 mkdirs()
File file3 = new File("/Users/zhangqiang/Desktop/demo/set/", "HashSet/test");
if(!file3.exists()) {
file3.mkdirs();
}
//创建文件
if(!file1.exists()) {
try {
file1.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4. 字节流概述
字节流是单个字节的操作,一般用于2进制中
- 字节输入流 InputStream
- 字节输出流 OutputStream
5. FileIntputStream
- 文件输入流 是从文件系统中的某个文件获得输入字节
- 用于读取诸如图像数据之类的原始字节流
方法名 | 说明 |
---|---|
public int read() | 从输入流中读取一个数据字节 |
public int read(byte[] b) | 从输入流中将最多 b.length 个字节的数据读入一个 byte 数组中 |
public int read(byte[] b, int off, int len) | 从输入流中将最多 len 个字节的数据读入 byte 数组中 |
public void close() | 关闭此文件输入流并释放与此流有关的所有系统资源 |
- read() 方法, 如果返回值为 -1, 则表示已经达到文件末尾!
6. FileIntputStream 例子
- 读txt中的内容
package com.imooc.file;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputDemo1 {
public static void main(String[] args) {
// 创建一个 FileInputStream 对象
try {
FileInputStream fis = new FileInputStream("imooc.txt");
/**
* 读取文件中的内容, 常规写法一
*/
int n = fis.read(); // 读取一个字符
while (n != -1) {
System.out.print((char) n);
n = fis.read();
}
/**
* 读取文件中的内容, 简写二
* while ((n = fis.read()) != -1) {
* System.out.println((char)n); \
* }
*/
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 把文件中的内容放到 byte数组中,然后输出
package com.imooc.file;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputDemo2 {
public static void main(String[] args) {
// 创建一个 FileInputStream 对象
try {
FileInputStream fis = new FileInputStream("imooc.txt");
// 把 txt中的内容放到 字节数组中
byte[] b = new byte[100];
fis.read(b);
System.out.println(new String(b));
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
7. FileOutputStrwam
常用方法
方法名 | 描述 |
---|---|
public void write(int b) | 将指定字节写入此文件输出流 |
public void write(byte[] b) | 将 b.length 个字节从指定 byte 数组写入此文件输出流中 |
public void write(byte[] b, int off, int len) | 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流 |
public void close() | 关闭此文件输出流并释放与此流有关的所有系统资源 |
package com.imooc.file;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputDemo {
public static void main(String[] args) {
FileOutputStream fos;
FileInputStream fis;
try {
fos = new FileOutputStream("imooc.txt", true);
fis = new FileInputStream("imooc.txt");
fos.write(50);
fos.write('a');
System.out.println(fis.read());
System.out.println((char) fis.read());
fos.close();
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件拷贝
package com.imooc.file;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputDemo1 {
public static void main(String[] args) {
// 文件拷贝
try {
// 输入流对象, 把文件读到字节中
FileInputStream fis = new FileInputStream("01.jpg");
// 输出流对象, 把字节写到文件中
FileOutputStream fos = new FileOutputStream("01copy.jpg");
int n = 0;
byte[] b = new byte[1024];
while ((n = fis.read(b)) != -1) {
fos.write(b, 0, n);
}
fis.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
8. 缓冲流概述
缓冲流可以提升速度
- 缓冲输入流 BufferedInputStream
- 缓冲输出流 BufferedOutputStream
9. 缓冲流案例
package com.imooc.file;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedDemo {
public static void main(String[] args) {
try {
FileOutputStream fos = new FileOutputStream("imooc.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
FileInputStream fis = new FileInputStream("imooc.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
// 开始时间, 长整型
long startTime = System.currentTimeMillis();
bos.write(50);
bos.write('a');
bos.flush();
// 结束时间,长整型
long endTime = System.currentTimeMillis();
// 计算时间差
System.out.println(endTime - startTime);
System.out.println(bis.read());
System.out.println((char) bis.read());
fos.close();
bos.close();
fis.close();
bis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
PS: 计算程序运行时间
// 开始时间, 长整型
long startTime = System.currentTimeMillis();
// ...
// 结束时间,长整型
long endTime = System.currentTimeMillis();
// 计算时间差
System.out.println(endTime - startTime);
10. 字符流概述
- 字符输入流 Reader
- 字符输出流 Writer
11. 字节字符转换流
- InputStreamReader
- OutputStreamWriter
package com.imooc.file;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class charstream {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("imooc.txt");
InputStreamReader isr = new InputStreamReader(fis, "UTF8");
FileOutputStream fos = new FileOutputStream("imooc1.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
// 读取数据
int n = 0;
char[] cbuf = new char[10];
// while ((n = isr.read()) != -1) {
// System.out.print((char) n);
// }
// while ((n = isr.read(cbuf)) != -1) {
// String s = new String(cbuf, 0, n);
// System.out.print(s);
// }
while ((n = isr.read(cbuf)) != -1) {
osw.write(cbuf, 0, n);
osw.flush();
}
fis.close();
fos.close();
isr.close();
osw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
12. 其他字符流
- BufferedReader
- BufferedWrite
package com.imooc.file;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class charstream {
public static void main(String[] args) {
try {
// 字节流
FileInputStream fis = new FileInputStream("imooc.txt");
// 字符流
InputStreamReader isr = new InputStreamReader(fis, "UTF8");
// 字符缓冲流
BufferedReader br = new BufferedReader(isr);
FileOutputStream fos = new FileOutputStream("imooc1.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
BufferedWriter bw = new BufferedWriter(osw);
// 读取数据
int n = 0;
char[] cbuf = new char[10];
// while ((n = isr.read()) != -1) {
// System.out.print((char) n);
// }
// while ((n = isr.read(cbuf)) != -1) {
// String s = new String(cbuf, 0, n);
// System.out.print(s);
// }
while ((n = br.read(cbuf)) != -1) {
bw.write(cbuf, 0, n);
bw.flush();
}
fis.close();
fos.close();
isr.close();
osw.close();
br.close();
bw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
13. 对象的序列化与反序列化
- 序列化: 把 Java对象转换为字节序列的过程.
- 反序列化: 把字节序列恢复为 Java 对象的过程.
对象序列化步骤:
- 创建一个类, 继承 Serializable 接口
- 创建对象
- 将对象写入文件
- 从文件读取对象信息
对象序列化涉及到2个类
- 对象输入流 ObjectInputStream
- 对象输出流 ObjectOutputStream
package com.imooc.serial;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class GoodsTest {
public static void main(String[] args) {
// 定义Goods类对象
Goods goods1 = new Goods("gd001", 3000, "电脑");
try {
// 写数据, 将对象信息写入文件
FileOutputStream fos = new FileOutputStream("imooc.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(goods1);
oos.flush();
// 读数据, 读Goods对象的信息
FileInputStream fis = new FileInputStream("imooc.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
try {
Goods goods = (Goods) ois.readObject();
System.out.println(goods);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
fos.close();
oos.close();
fis.close();
ois.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}