package cn.itcast.dao.impl;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import cn.itcast.dao.UserDao;
import cn.itcast.pojo.User;
/**
* 这是用户操作的具体实现类(IO版)
*
* @author 风清扬
* @version V1.1
*/
public class UserDaoImpl implements UserDao {
// 为了保证文件一加载就创建
private static File file = new File("user.txt");
static {
try {
file.createNewFile();
} catch (IOException e) {
System.out.println("创建文件失败");
// e.printStackTrace();
}
}
@Override
public boolean isLogin(String username, String password) {
boolean flag = false;
BufferedReader br = null;
try {
// br = new BufferedReader(new FileReader("user.txt"));
br = new BufferedReader(new FileReader(file));
String line = null;
while ((line = br.readLine()) != null) {
// 用户名=密码
String[] datas = line.split("=");
if (datas[0].equals(username) && datas[1].equals(password)) {
flag = true;
break;
}
}
} catch (FileNotFoundException e) {
System.out.println("用户登录找不到信息所在的文件");
// e.printStackTrace();
} catch (IOException e) {
System.out.println("用户登录失败");
// e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
System.out.println("用户登录释放资源失败");
// e.printStackTrace();
}
}
}
return flag;
}
@Override
public void regist(User user) {
/*
* 为了让注册的数据能够有一定的规则,我就自己定义了一个规则: 用户名=密码
*/
BufferedWriter bw = null;
try {
// bw = new BufferedWriter(new FileWriter("user.txt"));
// bw = new BufferedWriter(new FileWriter(file));
// 为了保证数据是追加写入,必须加true
bw = new BufferedWriter(new FileWriter(file, true));
bw.write(user.getUsername() + "=" + user.getPassword());
bw.newLine();
bw.flush();
} catch (IOException e) {
System.out.println("用户注册失败");
// e.printStackTrace();
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
System.out.println("用户注册释放资源失败");
// e.printStackTrace();
}
}
}
}
}
- 其他的流(了解),每种颜色代表一种大的流。
操作基本数据类型
(操作基本数据的流)
DataInputStream
DataOutputStream
内存操作流
操作字节数组
ByteArrayInputStream
ByteArrayOutputStream
操作字符数组
CharArrayReader
CharArrayWrite
操作字符串
StringReader
StringWriter
内存操作流一般用于处理临时信息,因为临时信息不需要保存,使用后就可以删除。
2:以第一个举例即可
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write("helloworld".getBytes());
baos.close();
byte[] bys = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(bys);
int by = 0;
while ((by = bais.read()) != -1) {
System.out.println((char) by);
}
baos.close();
bais.close();
最后的close()可以不写,通过看源码可以知道这里什么都没有做。
打印流概述
:只有写,没有读。
字节流打印流
字符打印流
打印流特点
只能操作目的地,不能操作数据源。
可以操作任意类型的数据。
如果启动了自动刷新,能够自动刷新。
可以操作文件的流
打印流复制文本文件
System类中的字段:in,out。(标准的输入输出流)
它们各代表了系统标准的输入和输出设备。
默认输入设备是键盘,输出设备是显示器。
System.in的类型是InputStream.
(字节流)System.out的类型是PrintStream(字符打印流)
是OutputStream的子类FilterOutputStream 的子类.
RandomAccessFile概述
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。支持对随机访问文件的读取和写入。【该类的构造方法的第二个参数就是读写文件的模式,一共有四种,用得最多的是rw】utf对应汉字的是3个字节,想读取就可以读取那个位置,是通过设置指针位置来实现的。
SequenceInputStream概述
(合并流)SequenceInputStream类可以将多个输入流串流在一起,合并为一个输入流,因此,该流也被称为合并流。
SequenceInputStream的构造方法
SequenceInputStream(InputStream s1, InputStream s2)
SequenceInputStream(Enumeration<? extends InputStream> e)
把多个文件的内容写入到一个文本文件
合并流的一个构造可以合并两个文件,有一个构造可以合并多个文件,但是需要注意的是,该多个流的合并需要用到vector集合的方法,所以就得创建该集合。
- 打印流如果启动了自动刷新,对于某些方法就可以不用刷新和换行了。(比如println())。 该流的底层也高效。
- 在jdk1.5之前,没有scanner这个类,为了使键盘输入数据就通过字符缓冲流包装标准输入流实现【BufferedReader br=new BufferedReader(new InputStream(System.in)).】
- 注意一点:字符流如果不能显示在控制台或者文件中,看看是否刷新缓冲区了。
- 序列化流:
序列化流
ObjectOutputStream
(写)反序列化流
ObjectInputStream
(读)序列化操作问题
为什么要实现序列化?
如何实现序列化?
序列化数据后,再次修改类文件,读取数据会出问题,如何解决呢?
使用transient关键字声明不需要序列化的成员变量
对象序列化是将对象状态转换为可保持或传输的过程。一般的格式是与平台无关的二进制流,可以将这种二进制流持久保存在磁盘上,也可以通过网络将这种二进制流传输到另一个网络结点。
对象反序列化,是指把这种二进制流数据还原成对象。
Properties概述
Properties作为Map集合的使用
,(但是不能有泛型)Properties的特殊功能
public Object setProperty(String key,String value)
【添加元素】public String getProperty(String key)
【获取元素】public Set<String> stringPropertyNames()
【获取所有键的集合】Properties和IO流的结合使用
public void load(Reader reader)
【把文件的数据读取到集合中】public void store(Writer writer,String comments)
【把集合中的数据存储到文件】注意这里的集合只能是Priperties.这个文件的数据也必须是键值对形式。该集合可以应用于单机游戏状态的存贮。
NIO其实就是新IO的意思。
JDK4出现NIO。新IO和传统的IO有相同的目的,都是用于进行输入输出的,但新IO使用了不同的方式来处理输入输出,采用内存映射文件的方式,将文件或者文件的一段区域映射到内存中,就可以像访问内存一样的来访问文件了,这种方式效率比旧IO要高很多,但是目前好多地方我们看到的还是旧IO的引用,所以我们仍以旧IO为主,知道NIO即可。Buffer(缓冲),Channer(通道)
JDK7要了解的新IO类
Path:与平台无关的路径。
Paths:包含了返回Path的静态方法。
public static Path get(URI uri):根据给定的URI来确定文件路径。
Files:操作文件的工具类。提供了大量的方法,简单了解如下方法
public static long copy(Path source, OutputStream out) :复制文件
public static Path write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options):
把集合的数据写到文件。
//复制文件
Files.copy(Paths.get("Demo.java"), newFileOutputStream("Copy.Java"));
//把集合中的数据写到文件
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
list.add("java");
Files.write(Paths.get("list.txt"), list, Charset.forName("gbk"));
- 要想实现序列化和反序列化就必须实现serializable接口,该接口中没有任何方法,在java中,没有任何方法的接口称为标记接口。
- 看到类实现了序列化接口的时候,要想解决黄色警告线的问题,就可以产生一个序列化id值,而且产生这个值以后,我们对类进行任何改动,它读取以前的数据是没有问题的。
- 打印流复制文件代码
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
PrintWriter pw = new PrintWriter(new FileWriter("b.txt"),true);
String line = null;
while((line=br.readLine())!=null) {
pw.println(line);
}
pw.close();
br.close();
- Jdk1.7新特性的代码【了解使用】
package cn.itcast_09;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
/*
* nio包在JDK4出现,提供了IO流的操作效率。但是目前还不是大范围的使用。
* 有空的话了解下,有问题再问我。
* JDK7的之后的nio:
* Path:路径
* Paths:有一个静态方法返回一个路径
* public static Path get(URI uri)
* Files:提供了静态方法供我们使用
* public static long copy(Path source,OutputStream out):复制文件
* public static Path write(Path path,Iterable<? extends CharSequence> lines,Charset cs,OpenOption... options)
*/
public class NIODemo {
public static void main(String[] args) throws IOException {
// public static long copy(Path source,OutputStream out)
// Files.copy(Paths.get("ByteArrayStreamDemo.java"), new
// FileOutputStream(
// "Copy.java"));
ArrayList<String> array = new ArrayList<String>();
array.add("hello");
array.add("world");
array.add("java");
Files.write(Paths.get("array.txt"), array, Charset.forName("GBK"));
}
}
- Properties的经典代码
package cn.itcast_08;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Properties;
/*
* 这里的集合必须是Properties集合:
* public void load(Reader reader):把文件中的数据读取到集合中
* public void store(Writer writer,String comments):把集合中的数据存储到文件
* 单机版游戏:
* 进度保存和加载。
* 三国群英传,三国志,仙剑奇侠传...
*
* 吕布=1
* 方天画戟=1
*/
public class PropertiesDemo3 {
public static void main(String[] args) throws IOException {
// myLoad();
myStore();
}
private static void myStore() throws IOException {
// 创建集合对象
Properties prop = new Properties();
prop.setProperty("林青霞", "27");
prop.setProperty("武鑫", "30");
prop.setProperty("刘晓曲", "18");
//public void store(Writer writer,String comments):把集合中的数据存储到文件
Writer w = new FileWriter("name.txt");
prop.store(w, "helloworld");
w.close();
}
private static void myLoad() throws IOException {
Properties prop = new Properties();
// public void load(Reader reader):把文件中的数据读取到集合中
// 注意:这个文件的数据必须是键值对形式
Reader r = new FileReader("prop.txt");
prop.load(r);
r.close();
System.out.println("prop:" + prop);
}
}
- 游戏设置次数的代码
package cn.itcast_08;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Properties;
/*
* 我有一个猜数字小游戏的程序,请写一个程序实现在测试类中只能用5次,超过5次提示:游戏试玩已结束,请付费。
*/
public class PropertiesTest2 {
public static void main(String[] args) throws IOException {
// 读取某个地方的数据,如果次数不大于5,可以继续玩。否则就提示"游戏试玩已结束,请付费。"
// 创建一个文件
// File file = new File("count.txt");
// if (!file.exists()) {
// file.createNewFile();
// }
// 把数据加载到集合中
Properties prop = new Properties();
Reader r = new FileReader("count.txt");
prop.load(r);
r.close();
// 我自己的程序,我当然知道里面的键是谁
String value = prop.getProperty("count");
int number = Integer.parseInt(value);
if (number > 5) {
System.out.println("游戏试玩已结束,请付费。");
System.exit(0);
} else {
number++;
prop.setProperty("count", String.valueOf(number));
Writer w = new FileWriter("count.txt");
prop.store(w, null);
w.close();
GuessNumber.start();
}
}
}
- 如果程序只有一条执行路径,那么该程序就是单线程程序,如果程序有多条【大于等于2】执行路径,那么该程序就是多线程程序。
- 进程就是正在运行的程序,多进程的意义就是提高cpu的使用率,在同一时间段同时运行,但是同一时间点是不可能的。
- 在同一个进程中又可以执行多个任务,而这每一个任务就可以看做是一个线程,多线程可以是进程拥有更大的几率抢到cpu资源,从而提高了程序的使用率。
- 我们不敢保证哪一个线程在某一时刻抢到资源,所以线程的执行是随机的。
- 并行(同一时间段)并发(同一时间点)
- Jvm的启动其实是多线程的,除了主线程之外,垃圾回收线程也会先启动,否则容易造成内存溢出,所以java虚拟机最低启动了两个线程。
- 实现多线程的2个方法【其实是3个】1.继承thread类,重写该类中的run()方法。2. 实现Runnable接口3.(了解)、使用ExecutorService、Callable、Future实现有返回结果的多线程
- 不是类中的所有代码都需要被(多)线程执行,这个时候,为了区分哪些代码能够被(多)线程执行,java就提供了thread类中的run()用来包含哪些被(多)线程执行的代码。 一般来说,被线程执行的代码都是比较耗时的,所以比较简单的就没有必要开创线程。
- Run()和start()方法的区别:run()仅仅是封装了线程执行的代码,直接调用跟普通方法一样。Start()方法首先启动了线程,然后再有jvm去调用该线程的run() 方法。
- 针对不是thread类的子类如何获取线程对象的名称?public static thread currenthread():返回当前线程的对象,然后在getname()即可。
- Java使用的是抢占模型:优先级高的获得cpu的时间片就多,同一优先级就随便获取一个。
- 线程的默认优先级是5.线程的优先级的范围是1~10,最小的是1,最大的是10.
- 线程的优先级高仅仅表示线程获取的cpu的时间片的几率高一些,但是要多次运行比较后才能得到比较好的效果,否则一两次有随机性,并不能很好的展现出结果。
- 线程的一些控制方法
线程休眠
public static void sleep(long millis)
【以毫秒为单位】
线程加入
【等待该线程终止,别的线程才可以抢】
public final void join()
线程礼让
【暂停当前正在执行的线程对象,并执行其他线程,它可以让多个线程的执行更和谐,但是不能保证一人一次】
public static void yield()
后台线程
【将该线程记为守护线程或用户线程,当正在运行的线程是守护线程时,java虚拟机退出,该方法必须在启动线程前调用】
public final void setDaemon(boolean on)
中断线程
public final void stop()
【让线程停止,已经过时了,不过还可以用,太过暴力,直接停止了,后面的代码就不能运行】
public void interrupt()
【中断线程,把线程的状态终止,并抛出一个异常,好让的代码运行】
- 线程的生命周期
- 实现runnable接口也同样要重写run()方法。然后创建实现类的对象,在创建thread类的对象,将实现类作为参数传递。
- 实现runnable接口的好处
实现接口方式的好处
可以避免由于Java单继承带来的局限性。
适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
- 为了实现共享一种资源,可以将这个资源定义为静态的。
- 加入延时后买票出现的问题
相同的票出现多次:
CPU的一次操作必须是原子性的
还出现了负数的票:
随机性和延迟导致的
package cn.itcast_08;
public class SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;
@Override
// public void run() {
// while (true) {
// // t1,t2,t3三个线程
// // 这一次的tickets = 100;
// if (tickets > 0) {
// // 为了模拟更真实的场景,我们稍作休息
// try {
// Thread.sleep(100); // t1就稍作休息,t2就稍作休息
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// System.out.println(Thread.currentThread().getName() + "正在出售第"
// + (tickets--) + "张票");
// // 理想状态:
// // 窗口1正在出售第100张票
// // 窗口2正在出售第99张票
// // 但是呢?
// // CPU的每一次执行必须是一个原子性(最简单基本的)的操作。
// // 先记录以前的值
// // 接着把ticket--
// // 然后输出以前的值(t2来了)
// // ticket的值就变成了99
// // 窗口1正在出售第100张票
// // 窗口2正在出售第100张票
//
// }
// }
// }
@Override
public void run() {
while (true) {
// t1,t2,t3三个线程
// 这一次的tickets = 1;
if (tickets > 0) {
// 为了模拟更真实的场景,我们稍作休息
try {
Thread.sleep(100); //t1进来了并休息,t2进来了并休息,t3进来了并休息,
} catch (InterruptedException e) {
e.printStackTrace();
}System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
//窗口1正在出售第1张票,tickets=0
//窗口2正在出售第0张票,tickets=-1
//窗口3正在出售第-1张票,tickets=-2
}
}
}
}
- 解决线程安全问题的基本思想
首先想为什么出现问题?(也是我们判断是否有问题的标准)
是否是多线程环境
是否有共享数据
是否有多条语句操作共享数据
如何解决多线程安全问题呢?
基本思想:让程序没有安全问题的环境。
怎么实现呢?
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
- 同步代码块(可以解决线程安全问题)
同步代码块
格式:
synchronized(对象){需要同步的代码;}
同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。(这个对象要形成唯一的一把”锁”,定义成成员变量为佳)
- 买票的完美代码:
package cn.itcast_10;
public class SellTicket implements Runnable {
// 定义100张票
private int tickets = 100;
// 定义同一把锁
private Object obj = new Object();
@Override
public void run() {
while (true) {
// t1,t2,t3都能走到这里
// 假设t1抢到CPU的执行权,t1就要进来
// 假设t2抢到CPU的执行权,t2就要进来,发现门是关着的,进不去。所以就等着。
// 门(开,关)
synchronized (obj) { // 发现这里的代码将来是会被锁上的,所以t1进来后,就锁了。(关)
if (tickets > 0) {
try {
Thread.sleep(100); // t1就睡眠了
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票 ");
//窗口1正在出售第100张票
}
} //t1就出来可,然后就开门。(开)
}
}
}
package cn.itcast_10;
/*
* 举例:
* 火车上厕所。
* 同步的特点:
* 前提:
* 多个线程
* 解决问题的时候要注意:
* 多个线程使用的是同一个锁对象
* 同步的好处
* 同步的出现解决了多线程的安全问题。
* 同步的弊端
* 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
*/
public class SellTicketDemo {
public static void main(String[] args) {
// 创建资源对象
SellTicket st = new SellTicket();
// 创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
// 启动线程
t1.start();
t2.start();
t3.start();
}
}
- 同步方法的格式以及锁对象。格式:将同步关键字写到方法上,它的锁对象是this(表示当前对象),同步代码块的锁对象是任意对象。同步静态方法的对象是当前类的class文件【类的字节码文件对象】,因为只有该class文件才会在静态方法之前。
如何把一个线程不安全的集合类变成一个线程安全的集合类
用Collections工具类的方法即可。
- Lock是一个接口。(子类实现然后调用lock()和unlock()方法,此功能就如同synchronized一样,这是jdk1.5的新特性,最好用try…finally语句,因为这样不管怎样都会释放锁)
- 同步可能产生死锁。
死锁是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
- 线程间通信问题:不同种类的线程间针对同一个资源的操作。
- 消费者和生产者问题要用到等待唤醒机制。【这些方法在object类中,这些方法的调用必须通过锁对象调用】------->>待续