集合到文件
public static void main(String[] args) throws IOException {
ArrayList<String> al =new ArrayList<String>();
al.add("林青霞");
al.add("张三丰");
al.add("李相赫");
BufferedWriter bw = new BufferedWriter(new FileWriter("untitled1//copy.txt"));
for (String s: al){
bw.write(s);
bw.newLine();
bw.flash();
}
bw.close();
}
文件到集合
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("untitled1//copy.txt"));
ArrayList<String> ar = new ArrayList<String>();
String line;
while ((line=br.readLine())!=null){
ar.add(line);
}
for (String s : ar){
System.out.println(s);
}
br.close();
}
点名器
public static void main(String[] args) throws IOException {
int m=0;
BufferedReader br = new BufferedReader(new FileReader("untitled1//copy.txt"));
ArrayList<String> ar = new ArrayList<String>();
String line;
while ((line=br.readLine())!=null){
ar.add(line);
m++;
}
br.close();
Random r = new Random();
int num = r.nextInt(m-1);
System.out.println(ar.get(num));
}
集合到文件
StringBuilder sb = new StringBuilder();
sb.append(s.getId()).append(s.getName()).append(s.getAge()).append(s.getAddress());
bw.write(sb.toString());
public static void main(String[] args) throws IOException {
ArrayList<Student> ar = new ArrayList<Student>();
Student s1 = new Student("tongxue01","小米","20","北京");
Student s2 = new Student("tongxue02","小明","18","西安");
Student s3 = new Student("tongxue03","小白","19","成都");
ar.add(s1);
ar.add(s2);
ar.add(s3);
int m = 10;
BufferedWriter bw = new BufferedWriter(new FileWriter("untitled1//copy.txt"));
for (Student s : ar){
bw.write(s.getId());
bw.write(",");
bw.write(s.getName());
bw.write(",");
bw.write(s.getAge());
bw.write(",");
bw.write(s.getAddress());
bw.newLine();
bw.flush();
}
bw.close();
}
文件到集合
public static void main(String[] args) throws IOException {
ArrayList<Student> ar = new ArrayList<Student>();
BufferedReader br = new BufferedReader(new FileReader("untitled1//copy.txt"));
String line;
while((line = br.readLine())!=null){
String[] sp = line.split(",");
ar.add(new Student(sp[0],sp[1],sp[2],sp[3]));
}
br.close();
for (Student s : ar){
System.out.println(s.getId()+","+s.getName()+","+s.getAge()+","+s.getAddress());
}
}
集合到文件带排序
public static void main(String[] args) throws IOException {
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int ss = o1.getChinese()+o1.getMath()+o1.getEnglish();
int sm = o2.getChinese()+o2.getMath()+o2.getEnglish();
int num = sm-ss;
int num2 = num==0?o1.getName().compareTo(o2.getName()):num;
return num2;
}
});
Student s1 = new Student("小米",88,79,95);
Student s2 = new Student("小白",85,77,86);
Student s3 = new Student("小张",92,90,96);
Student s4 = new Student("小李",81,99,72);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
BufferedWriter bw = new BufferedWriter(new FileWriter("untitled1//copy.txt"));
for (Student s : ts){
StringBuilder sb = new StringBuilder();
sb.append(s.getName()).append(",").append(s.getChinese()).append(",").append(s.getMath()).append(",").append(s.getEnglish());
bw.write(sb.toString());
bw.newLine();
bw.flush();
}
bw.close();
}
复制单级文件夹
public static void main(String[] args) throws IOException {
File srcFolder = new File("G://game");
String srcFolderName = srcFolder.getName();
File destFolder = new File("untitled1//",srcFolderName);
if (!destFolder.exists()){
destFolder.mkdir();
}
File[] files = srcFolder.listFiles();
for (File srcFile : files){
String srcName = srcFile.getName();
File destFile = new File(destFolder,srcName);
copyFile(srcFile,destFile);
}
}
private static void copyFile(File srcFile, File destFile) throws IOException{
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bys = new byte[1024];
int len;
while ((len = bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
复制多级文件夹
public static void main(String[] args) throws IOException {
File srcFile = new File("G:\\game");
File destFile = new File("untitled1\\");
copyFolder(srcFile,destFile);
}
private static void copyFolder(File srcFile, File destFile)throws IOException {
//判断数据源File是否是目录
if (srcFile.isDirectory()){
String srcFileName = srcFile.getName();
File newFolder = new File(destFile,srcFileName);
if (!newFolder.exists()){
newFolder.mkdir();
}
//获取数据源File下所有文件或者目录的File数组
File[] files = srcFile.listFiles();
for (File Fl : files){
//递归调用
copyFolder(Fl,newFolder);
}
}else {
File newFile = new File(destFile,srcFile.getName());
copyFile(srcFile,newFile);
}
}
private static void copyFile(File srcFile, File destFile) throws IOException{
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bys = new byte[1024];
int len;
while ((len = bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
复制文件的异常处理
try..catch...finally
try{
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}finally{
执行所有清除操作;
}
private static void method(){
FileReader fr = null;
FileWriter fw = null;
try{
fr = new FileReader("G:\\game");
fw = new FileWriter("D:\\game");
char[] chs = new char[1024];
int len;
while((len = fr.read(chs))!=-1){
fw.write(chs,0,len);
}
}catch(IOException e){
e.printStackTrace();
}finally {
if (fw!=null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw!=null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
JDK7改进方案:
try(定义流对象){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}
自动释放资源
private static void method2(){
try( FileReader fr = new FileReader("G:\\game");
FileWriter fw = new FileWriter("D:\\game");) {
char[] chs = new char[1024];
int len;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
}catch (IOException e){
e.printStackTrace();
}
}
JDK9改进方案
定义输入流对象;
定义输出流对象;
try(输入流对象;输出流对象){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}
自动释放资源
private static void method2()throws IOException{
FileReader fr = new FileReader("G:\\game");
FileWriter fw = new FileWriter("D:\\game");
try(fr;fw) {
char[] chs = new char[1024];
int len;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
}catch (IOException e){
e.printStackTrace();
}
}
标准输入输出流
System类中有两个静态的成员变量:
public static final InputStream in:标准输入流,通常该流对应于键盘输如或由主机环境或用户指定的另一个输入源
public static final InputStream out:标准输出流,通常该流对应于显示输出或主机环境或用户指定的另一个输出目标(final修饰 表明是一个常量,static说明可以通过类名直接访问)
输入流
public static void main(String[] args) throws IOException {
// InputStream in = System.in;
// int by;
// while ((by=in.read())!=-1){
// System.out.println((char) by);
// }
//如何把字节流转换成字符流?转换流
// InputStreamReader isr = new InputStreamReader(in);
// BufferedReader br = new BufferedReader(isr);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入字符串");
String line = br.readLine();
System.out.println(line);
System.out.println("输入一个整数");
int i = Integer.parseInt(br.readLine());
System.out.println(i);
// Scanner sc = new Scanner(System.in);
// int i1 = sc.nextInt();
}
自己实现键盘录入数据: BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
java提供一个类实现键盘录入
Scanner sc = new Scanner(System.in);
输出流
PrintStream out = System.out;
out.print("hello");
out.println("world");
输出语句的本质:是一个标准的输出流
PrintStream ps = System.out;
PrintStream类有的方法,System.out都可以使用
打印流
打印流分类:字节打印流:PrintStream(字节输出流)
字符打印流:PrintWriter(字节输入流)
打印流的特点:
只负责输出数据,不负责读数据;有自己的特有方法
字节打印流:PrintStream(String fileName):使用指定的文件名创建新的打印流;使用继承父类的方法写数据,查看的时候会转码,使用自己的特有方法写数据,查看的数据原样输出
PrintStream ps = new PrintStream("untitled1\\copy.txt");
//写数据
//字节输出流的方法
// ps.write(88);
// ps.close();
//使用特有方法写数据
ps.println(98);
ps.print(88);
ps.close();
字符打印流:PrintWriter(String fileName) 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
PrintWriter(Writer out,boolean autoFlush) 创建一个新的PrintWriter out:字符输出流
autoFlush:一个布尔值,如果为真,则println,
printf,或format方法将刷新输出缓冲区。
public static void main(String[] args) throws IOException {
// PrintWriter pw = new PrintWriter("untitled1\\copy.txt");
// pw.write("hello");
// pw.write("\r\n");
// pw.flush();
// pw.println("hello");
/*
pw.write("hello");
pw.write("\r\n");
*/
// pw.flush();
// pw.println("world");
// pw.flush();
PrintWriter pw = new PrintWriter(new FileWriter("untitled1\\copy.txt"),true);
pw.println("hello");
/*
pw.write("hello");
pw.write("\r\n");
pw.flush();
*/
// PrintWriter pw = new PrintWriter(new FileWriter("untitled1\\copy.txt"),false);
// pw.println("hello");
pw.close();
}
复制java文件(打印流)
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("untitled1\\untitled1.java"));
PrintWriter pw = new PrintWriter(new FileWriter("untitled1\\copy.txt"),true);
String line;
while ((line = br.readLine())!=null){
pw.println(line);
}
pw.close();
br.close();
}
对象序列化流
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中储存的属性等信息,字节序列写到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
要实现序列化和反序列化就要使用对象序列化流和对象反序列化列流:
对象序列化流:ObjectOutputStream
将java对象的原始数据类型和图形写入OutputStream,可以使用ObjectInputStream读取(重构)对象,可以通过使用流的文件来实现对象的持久存储,如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
构造方法:ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:void writeObject(Object obj):将指定的对象写入ObjectOutputStream
注意:一个对象想要被序列化,该对象所属的类必须实现Serializable接口
Serializable是一个标记接口,实现该接口,不需要重写任何方法
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("untitled1\\copy.txt"));
Student s = new Student("xiaoliu",18);
oos.writeObject(s);
oos.close();
对象反序列化流:ObjectInputStream
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法:
ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
反序列对象的方法:
ObjectreadStream():从ObjectInputStream读取一个对象
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("untitled1\\copy.txt"));
Object o = ois.readObject();
Student s = (Student)o;
System.out.println(s.getName()+","+s.getAge());
ois.close();
}
1. 当对象序列化流序列化一个对象后,我们修改了对象所属文件,读取数据时会报错(抛出InvalidClassException异常),此时,我们应该给对象所属的类加一个serialVersionUID 值:private static fianl long serialVersionUID = 42L;
2. 如果一个对象中的某个成员变量的值不想被序列化,如何实现?
把对应的成员变量用 transient 关键字修饰即可(不参与序列化过程) 例:private transient int age
Properties:是一个Map体系的集合类
Properties可以保存到流中或从流中加载
public static void main(String[] args) {
Properties pt = new Properties();
pt.put("xiaoming","0101");
pt.put("xiaobai","0202");
Set<Object> objects = pt.keySet();
for (Object ob : objects){
System.out.print(ob+",");
System.out.println(pt.get(ob));
}
}
Properties作为集合的特有方法:
Object setProperty(String key,String value) 设置集合的键和值,都是String类型,底层调用
Hashtable方法put
String getProperty(String key) 使用此属性列表中指定的键搜索属性(值)
Set<String>stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键
及其对应的值是字符串
public static void main(String[] args) {
Properties pt = new Properties();
pt.setProperty("tongxue01","xiaoming");
pt.setProperty("tongxue02","xiaobai");
// System.out.println(pt.getProperty("tongxue02"));
Set<String> names = pt.stringPropertyNames();
for (String s : names){
System.out.println(s);
String property = pt.getProperty(s);
System.out.println(s+","+property);
}
}
Properties和IO流结合的方法
void load(InputStresam inStream) 从输入字节流读取属性列表(键和元素对)
void load(Reader reader) 从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合于load(InputStream)方法的格式写入输出字节流
void store(Writer writer,String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合使用load(reader)方法的格式写入输出字符流
public static void main(String[] args) throws IOException {
//集合保存到文件
myStore();
//文件中数据加载到集合
myLoad();
}
private static void myLoad() throws IOException {
Properties pt = new Properties();
FileReader fr = new FileReader("untitled1\\copy.txt");
pt.load(fr);
fr.close();
System.out.println(pt);
Set<String> names = pt.stringPropertyNames();
for (String s : names){
System.out.println(s+","+pt.getProperty(s));
}
}
private static void myStore() throws IOException {
Properties pt = new Properties();
pt.setProperty("tongxue01","xiaobai");
pt.setProperty("tongxue02","xiaohuang");
FileWriter fileWriter = new FileWriter("untitled1\\copy.txt");
pt.store(fileWriter,null);
fileWriter.close();
}
猜数字小游戏
private Game(){
}
public static void start(){
Random r = new Random();
int i1 = r.nextInt(100);
while (true){
Scanner sc = new Scanner(System.in);
System.out.println("输入你猜的数字:");
int i = sc.nextInt();
if (i>i1){
System.out.println("你猜的数字较大");
}else if (i<i1){
System.out.println("你猜的数字较小");
}else{
System.out.println("恭喜你猜中了");
break;
}
}
}
public static void main(String[] args) throws IOException {
Properties pt = new Properties();
FileReader fr = new FileReader("untitled1\\copy.txt");
pt.load(fr);
fr.close();
String count = pt.getProperty("count");
//字符串类型转整数类型
int i = Integer.parseInt(count);
if (i>=3){
System.out.println("游戏试玩结束");
}else {
Game.start();
i++;
//整数类型转字符串类型
pt.setProperty("count",String.valueOf(i));
FileWriter fw = new FileWriter("untitled1\\copy.txt");
pt.store(fw,null);
fw.close();
}
}
进程和线程
1.1进程:是正在运行的程序
是系统进行组员分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
1.2线程
线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序
多线程实现方式1:
方式1:继承Thread类
定义一个类MyThread继承Thread类
在MyThread类中重写run()方法
创建MyThread类的对象
启动线程
为什么要重写run()方法
因为run()是用来封装被线程执行的代码
run()方法和start()方法的区别
run():疯传线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后又jvm调用此线程的run()方法
设置和获取线程名称
Thread类中设置和获取线程名称的方法
void setName(String name):将此线程的名称更改为等于参数name
String getName():返回此线程的名称
Thread(String name);写入线程名称
static Thread currentThread();返回对当前正在执行的线程对象的引用
System.out.println(Thread.currentThread().getName());
线程调度
线程有两种调度模型
分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选
择一个,优先级高的线程获取的CPU时间片相对多一些
JAVA使用的是抢占式调度模型
加入计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行命令。所以说多线程程序的执行是由随机性的,因为谁抢到CPU的使用权是不一定的
Thred类中设置和获取线程优先级的方法
public final int getPriority();返回此线程的优先级
public final void setPriority(int newPriority);更改此线程的优先级
System.out.println(Thread.MAX_PRIORITY); //最大10
System.out.println(Thread.MIN_PRIORITY); //最小1
System.out.println(Thread.NORM_PRIORITY); //默认5
线程默认优先级是5,线程优先级范围1-10;
线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的效果
线程控制
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数
void join() 等待这个线程死亡(最优先线程)
void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,JVM将退出
@Override
public void run() {
for (int i = 0;i<=100;i++){
System.out.println(getName()+":"+i);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
m1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
public static void main(String[] args) {
MyThread m1 = new MyThread("T1");
MyThread m2 = new MyThread("T2");
//设置主线程
Thread.currentThread().setName("T3");
//设置守护线程
m1.setDaemon(true);
m2.setDaemon(true);
m1.start();
m2.start();
for (int i = 0;i<=10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
多线程实现方式2
方式2:实现Runnable接口
定义一个类MyRunnable实现Runnable接口
在MyRunnable类中重写run()方法 Thread(Runnable target)
创建MyRunnable类的对象
创建Thread类的对象,把MyRunnable对象作为构造方法的参数
启动线程
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0;i<=100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public static void main(String[] args) {
MyRunnable mr1 = new MyRunnable();
// Thread t1 = new Thread(mr1);
// Thread t2 = new Thread(mr1);
//Thread(Runnable target,String name);
Thread t1 = new Thread(mr1,"T1");
Thread t2 = new Thread(mr1,"T2");
t1.start();
t2.start();
}
多线程实现方式有两种
继承Thread类
实现Runnable接口
相比继承Thread类,实现Runnable接口的好处
避免了Java单继承的局限性
适合多个相同程序的代码去处理同一个资源的情况,八线程和程序的代码,数据有效分离,较好地体现了面向对象的设计思想。
案例:买票
public static void main(String[] args) {
MyRunnable mr1 = new MyRunnable();
Thread t1 = new Thread(mr1,"T1");
Thread t2 = new Thread(mr1,"T2");
t1.start();
t2.start();
}
public class MyRunnable implements Runnable{
int i =10;
@Override
public void run() {
while (true) {
if (i > 0) {
System.out.println(Thread.currentThread().getName() + "第"+i+"票");
i--;
} else {
System.out.println("票已售完");
break;
}
}
}
}
解决多线程安全问题
基本思想:让程序没有安全问题的环境
实现:将多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行(Java提供同步代码块)
同步代码块
锁多条语句操作共享数据,使用同步代码块实现
格式:
synchronized(任意对象){
多条语句操作共享数据的代码;
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
public class MyRunnable implements Runnable{
private int i =10;
private Object obj = new Object();
@Override
public void run() {
synchronized (obj) {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i > 0) {
System.out.println(Thread.currentThread().getName() + "还有" + i + "张");
i--;
} else {
System.out.println("票已售完");
break;
}
}
}
}
}
同步的好处和弊端
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
线程安全的类
StringBuffer
线程安全,可变的字符序列
从版本JDK5开始,被StringBuilder代替,通常应该使用StringBuilder类,因为它支持所有相同的操作,但他更快,因为他不执行同步
Vector
从Java 2平台v1.2开始,该类改进了List接口,使其成为Java Collectoons Framework的成员。与新的集合实现不同,Vector被同步,如果不需要线程安全的实现,建议使用ArrayList代替Vector
Hashtable
该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或者值
从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java CollectionsFramework的成员。与新的集合实现不同,Hashtable被同步,如果不需要线程安全的实现,建议使用HashMap代替Hashtable
//static<T> List<T> synchronizedList(List<T> List); 返回由指定列表支持的同步(线程安全)列表
Collections.synchronizedList(new ArrayList<String>());
Lock锁
为了更清晰地表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
void lock() 获得锁
void unlock() 释放锁
Lock是接口不能直接实例化,这里采用他的实现类ReentrantLock来实例化
ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例
public class MyRunnable implements Runnable{
private int i =10;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try{
if (i > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "还有" + i + "张");
i--;
} else {
System.out.println("票已售完");
break;
}
}finally {
lock.unlock();
}
}
}
}
生产者消费者模式概述
为了体现生产和消费过程中的等待和唤醒,Java就提供了几个方法供我们使用,这几个方法在Object类中Object类的等待和唤醒方法:
void wait() 导致当前线程等待,指代另一个线程调用该对象的notify()方法或notifyAll()方法
void notify() 唤醒正在等待对象监视器的单个线程
void notifyAll() 唤醒正在等待对象监视器的所有线程
案例(生产者消费者)
public class Box {
private int milk;
private boolean state = false;
public synchronized void putmilk(int milk){
if (state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.milk = milk;
System.out.println("放入"+milk+"瓶牛奶");
state = true;
//唤醒等待的线程
notifyAll();
}
public synchronized void getmilk(){
if(!state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("拿到第"+milk+"瓶奶");
state = false;
notifyAll();
}
}
public class Producer implements Runnable{
private Box b;
public Producer(Box b) {
this.b = b;
}
@Override
public void run() {
for (int i=1;i<=5;i++){
b.putmilk(i);
}
}
}
public class Customer implements Runnable {
private Box b;
public Customer(Box b) {
this.b = b;
}
@Override
public void run() {
while (true){
b.getmilk();
}
}
}
public class BoxDemo {
public static void main(String[] args) {
Box b =new Box();
Producer p = new Producer(b);
Customer c = new Customer(b);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
网络编程
网络编程概述:在网络协议下,实现网络互联的不同计算机上运行的程序间可以进行数据交换
网络编程三要素:
IP地址:要想让网络中的计算机能够互相通信,必须为每台计算机制定一个标识号,通过这个标识号来指定要接受数据的计算机和识别发送的计算机,而IP地址就是这个标识号,也就是设备的标识
端口:网络的通信,本质上是两个应用程序的通信,每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说IP地址可以唯一识别网络中的设备,那么端口号就可以唯一识别设备中的应用程序,也就是应用程序的标识
协议:通过计算机网络可以使多条台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,在计算机网络中,这些连接和通信的规则被称为网络通信协议,他对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议由UDP协议和TCP协议
IP地址分为两大类
IPv4:是给每个连接在网络上的主机分配一个32bit地址。按照TCP/IP规定,IP地址用二进制标识,每个IP地址长32bit,也就是4个字节。例如采用二进制形式的IP地址时“11000000 10101000 000000001 01000010”,太长处理起来很麻烦,为了方便使用,IP地址经常被写成十进制的形式,中间使用符号“.”分割不同的字节。于是,上面的IP地址可以表示为“192.168.1.66”.IP地址的这种表示法叫做“点分十进制表示发”,这显然比1和0容易记忆
IPv6:由于互联网的蓬勃发展,IP地址的需求量越来越大,但是网络地址资源有限,是的IP分配越发紧张,为了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组16进制数,这样就解决了网络地址资源数量不够的问题
常用命令:ipconfig:查看本机IP地址
ping IP地址:检查网络是否连通
特殊IP地址:
127.0.0.1:是回送地址,可以代表本机地址,一般用来测试使用
InetAddress的使用
InetAddress:此类标识Internet协议(IP)地址
static InetAddress getByName(String host) 确定主机名称的IP地址,主机名称可以是及其名称,也可以是 IP地址
String getHostName() 获取此IP地址的主机名
String getHoseAddress() 返回文本显示中的IP地址字符串
InetAddress address = Inet4Address.getByName("m");
String hostName = address.getHostName();
String hostAddress = address.getHostAddress();
System.out.println(hostName);
System.out.println(hostAddress);
端口号:用两个字节表示的整数,他的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另一个服务或应用所占用,会导致当前程序启动失败
协议:计算机网络中连接和通信的规则被称为网络通信协议
UDP协议:用户数据报协议(User Datagram Protocol)
UDP是无连接通信协议,即在数据传输时,输一局的发送端和接收端不建立逻辑连接。简单来说,当一台计算机像另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在接收到数据时,也不会像发送端反馈是否收到数据
由于使用DUP协议消耗资源少,通信效率高,所以通常会用于音频、视频和普通数据的传输(不建议用来传输重要数据)
TCP协议:传输控制协议(Transmission Control Protocol)
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务器端发出连接请求,每次链接的创建都需要经过“三次握手”
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
第一次握手,客户端向服务器端发出连接请求,等待服务器确认
第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求
第三次握手,客户端再次向服务器端发送确认信息,确认连接
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了,由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如上传文件,下载文件、浏览网页等