泛型
- 泛型是JDK1.5提出的
- 泛型类型必须是类,不能是基本数据类型
-
List
、List 如果一个泛型类中需要传递一个List
- 如果List
能作为List 的父类 - 创建一个List
或List 存放到List 中 - 那么用get获取数据时,获取到的是Integer、float还是Number类型的,如果是这样还要进行类型转化
- 这违背了定义泛型的初衷
- 所以List
和List 不看做有父子关系
那么如果我们需要获取不同类型的数据,难道需要编写获取不同类型的方法吗?这违背了多态的特性
类型通配符<?>可以很好的解决这个问题
public static void getData(Box<?> data) {
System.out.println("data :" + data.getData());
}
也可以通过<? extends Number> 与 <? super Number> 控制类型通配符的上限和下限
多线程
程序、进程、线程
- 程序:一段静态代码
- 进程:正在运行的一个程序
-
线程的创建
继承Thread
public class MyThread extends Thread{
@Override
public void run(){
System.out.println("创建线程~启动线程调用run方法");
}
}
实现Runnable接口
public class Hello implements Runnable{
@Override
public void run(){
System.out.println("实现Runnable接口");
}
}
线程中常用方法
start():启动线程,执行run()
- run():执行的操作在run()方法中
- currentThread():静态方法,当前线程
- getName():获取线程名
- setName():设置线程名
- yield():释放CPU使用权
- join():线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
- stop():已过时,强制结束线程
- sleep():休眠,进入阻塞,同步代码块中不会释放锁
- isAlive():线程是否存活
- getPriority():获取优先级 1~10,默认5
-
线程的同步
同步代码块(synchronized)
synchronized(对象/同步监视器){
...
}
同步方法(synchronized)
public synchronized void show(String name){
...
}
Lock(锁)
jdk5.0开始,显示锁
- java.util.concurrent.locks.Lock接口是控制多个线程进行共享资源的工具
- ReentrantLock类实现了Lock,可以加锁,释放锁
```java
class A{
private final ReentrantLock lock = new ReentrantLock();
public void m(){
}lock.lock();
try{
//保证线程安全的代码
}catch(){
...
}finally{
lock.unlock();
}
}
<a name="E8E7v"></a>
### 单例模式(饿汉式,线程安全)
```java
class Singleton{
private Singleton(){}
private static Singleton instance = null;
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
线程的通信
- wait():线程阻塞,释放锁,进入等待
- notify()/notifyAll():唤醒等待该对象监控权(锁)的一个/所有线程,有这个锁才能调用
使用两个线程交替打印1~100
//使用两个线程打印1~100,交替打印
class Number implements Runnable{
private int number = 1;
private Object obj = new Object();
@Override
public void run(){
while(true){
synchronized(obj){
obj.notify();
if(number <= 100){
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try{
//使这个线程进入阻塞:释放锁
obj.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
生产者消费者问题
//生产者消费者问题
class Clerk{
private int productCount = 0;
//生产产品
public synchronized void produceProduct(){
if(productCount < 20){
productCount++;
System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "个产品");
notify();
}else{
try{
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct(){
if(productCount > 0){
System.out.println(Thread.currentThread().getName() + ":开始消费第" + productCount + "个产品");
productCount--;
notify();
}else{
try{
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
//生产者
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run(){
System.out.println(getName() + ":开始生产产品.....");
while(true){
try{
sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
//消费者
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run(){
System.out.println(getName() + ":开始消费产品.....");
while(true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
JDK5.0新增创建线程方式
实现Callable接口
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,比如获取返回结果
```java
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
} } public class ThreadNew { public static void main(String[] args) {int sum = 0;
for (int i = 1; i <= 100; i++) {
if(i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
try {
//6.获取Callable中call方法的返回值
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Object sum = futureTask.get();
System.out.println("总和为:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
<a name="m1rSJ"></a>
#### 线程池
![QQ截图20210722105014.png](https://cdn.nlark.com/yuque/0/2021/png/22164924/1626922257732-d68986ef-0fbc-4114-9070-72bf6d6beb99.png#clientId=u7ed34dea-9058-4&from=ui&id=ua58e7a2e&margin=%5Bobject%20Object%5D&name=QQ%E6%88%AA%E5%9B%BE20210722105014.png&originHeight=406&originWidth=847&originalType=binary&ratio=1&size=225850&status=done&style=none&taskId=ud88f225e-9cfa-4afc-89c6-8d71be29f56)![QQ截图20210722105035.png](https://cdn.nlark.com/yuque/0/2021/png/22164924/1626922257800-c6791fa8-b747-4c4e-860b-29d9c273bdde.png#clientId=u7ed34dea-9058-4&from=ui&id=u35c2fbb8&margin=%5Bobject%20Object%5D&name=QQ%E6%88%AA%E5%9B%BE20210722105035.png&originHeight=472&originWidth=881&originalType=binary&ratio=1&size=263737&status=done&style=none&taskId=u8c9a60bf-d9c5-47ef-833c-15258c9e912)
<a name="jbAau"></a>
## Java输入输出流
<a name="jAfEd"></a>
### 字节流
<a name="NNAsu"></a>
#### 字节输入流
- InputStream接口(抽象基类)
- FileInputStream文件输入流
<a name="py48G"></a>
#### 字节输出流
- OutputStream接口(抽象基类)
- FileOutputStream文件输出流
<a name="snZqh"></a>
#### 复制文件
```java
public void copyFile(String srcPath,String destPath){
FileInputStream = null;
FileOutputStream = null;
try{
File src = new File("srcPath");
File dest = new File("destPath");
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int len;
while((len = fis.read(buf)) != -1){
fos.write(buf, 0, len);
}
}catch(IOException e){
e.printStackTrace();
}finally{
if(fis != null){
try{
fis.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(fos != null){
try{
fos.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符流
字符输入流
- Reader接口(抽象基类)
-
字符输出流
Writer接口(抽象基类)
-
缓冲流
提供流的读取、写入速度
提高读写速度的原因:内部提供了一个缓冲区 BufferInputStream
- BufferOutputStream
- BufferReader
BufferWriter
//实现文件复制的方法
public void copyFileWithBuffered(String srcPath,String destPath){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.造文件
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//2.造流
//2.1 造节点流
FileInputStream fis = new FileInputStream((srcFile));
FileOutputStream fos = new FileOutputStream(destFile);
//2.2 造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.复制的细节:读取、写入
byte[] buffer = new byte[1024];
int len;
while((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
//要求:先关闭外层的流,再关闭内层的流
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
// fos.close();
// fis.close();
}
}
处理流之转换流
InputStreamReader:将InputStream转换为Reader
OutputStreamWriter:将Writer转换为OutputStream
对象序列化
实现Serializable接口 ```java /**
- 对象流的使用
- 1.ObjectInputStream 和 ObjectOutputStream
- 2.作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。 *
- 3.要想一个java对象是可序列化的,需要满足相应的要求。见Person.java *
- 4.序列化机制:
- 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种
- 二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
- 当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。
*
- @author shkstart
@create 2019 上午 10:27 */ public class ObjectInputOutputStreamTest {
/ 序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去 使用ObjectOutputStream实现 / @Test public void testObjectOutputStream(){
ObjectOutputStream oos = null;
try {
//1.
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
//2.
oos.writeObject(new String("我爱北京天安门"));
oos.flush();//刷新操作
oos.writeObject(new Person("王铭",23));
oos.flush();
oos.writeObject(new Person("张学良",23,1001,new Account(5000)));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(oos != null){
//3.
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/ 反序列化:将磁盘文件中的对象还原为内存中的一个java对象 使用ObjectInputStream来实现 / @Test public void testObjectInputStream(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj = ois.readObject();
String str = (String) obj;
Person p = (Person) ois.readObject();
Person p1 = (Person) ois.readObject();
System.out.println(str);
System.out.println(p);
System.out.println(p1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} }
```java
/**
* Person需要满足如下的要求,方可序列化
* 1.需要实现接口:Serializable
* 2.当前类提供一个全局常量:serialVersionUID
* 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性
* 也必须是可序列化的。(默认情况下,基本数据类型可序列化)
*
*
* 补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
*
*
* @author shkstart
* @create 2019 上午 10:38
*/
public class Person implements Serializable{
public static final long serialVersionUID = 475463534532L;
private String name;
private int age;
private int id;
private Account acct;
public Person(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
public Person(String name, int age, int id, Account acct) {
this.name = name;
this.age = age;
this.id = id;
this.acct = acct;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
", acct=" + acct +
'}';
}
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 getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {}
}
class Account implements Serializable{
public static final long serialVersionUID = 4754534532L;
private double balance;
@Override
public String toString() {
return "Account{" +
"balance=" + balance +
'}';
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public Account(double balance) {
this.balance = balance;
}
}
随机存储文件流(RandomAccessFile类)
- 可以当输入流也可以当输出流
- 构造器
- public RandomAccessFile(File file, String mode)
- public RandomAccessFile(String name, String mode)
- mode参数
- r:只读
- rw:读写
- rwd:读写,同步文件内容更新
- rws:读写,同步文件内容和元数据的更新
- 文件不存在,创建;文件存在(默认覆盖)