File类介绍
File类的构造方法
方法名 | 说明 |
---|---|
File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 |
File(String parent, String child) | 从父路径名字符串和子路径名字符串创建新的 File实例 |
File(File parent, String child) | 从父抽象路径名和子路径名字符串创建新的 File实例 |
File f1 = new File("E:\\test\\java.txt");
File f2 = new File("E:\\test", "java.txt");
File f3 = new File("E:\\test");
File f4 = new File(f3,"java.txt");
方法分类
方法名 | 说明 |
---|---|
public boolean createNewFile() | 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件 |
public boolean mkdir() | 创建由此抽象路径名命名的目录 |
public boolean mkdirs() | 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 |
判断功能
方法名 | 说明 |
---|---|
public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
获取功能
方法名 | 说明 |
---|---|
public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转换为路径名字符串 |
public String getName() | 返回由此抽象路径名表示的文件或目录的名称 |
public String[] list() | 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 |
public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |
// 获取文件名
String[] strArray = f2.list();
for(String str : strArray) {
System.out.println(str);
}
// 获取文件对象
File[] fileArray = f2.listFiles();
for(File file : fileArray) {
if(file.isFile()) {
sout(file.getName());
}
}
方法分类
方法名 | 说明 |
---|---|
public boolean delete() | 删除由此抽象路径名表示的文件或目录 |
字节流抽象基类
字节输出流
使用字节输出流写数据的步骤
方法 | 功能 |
---|---|
FileOutputStream(String name) | 创建文件输出流以指定的名称写入文件 |
void close() | 关闭此文件输出流并释放与此流相关联的任何系统资源。 |
void write(int b) | 将指定的字节写入此文件输出流 |
FileOutputStream fos = new FileOutputStream();
fos.wirte(97);
fos.close();
写数据的方法分类
方法名 | 说明 |
---|---|
void write(int b) | 将指定的字节写入此文件输出流 一次写一个字节数据 |
void write(byte[] b) | 将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据 |
void write(byte[] b, int off, int len) | 将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据 |
字节流写数据如何实现换行
字节流写数据如何实现追加写入
FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt",true);
// 写数据
for (int i = 0; i < 10; i++) {
fos.write("hello".getBytes());
fos.write("\r\n".getBytes());
}
fos.close();
字节输入流
字节输入流读取数据的步骤
方法 | 功能 |
---|---|
FileInputStream(String name) | 创建字节输入流对象 |
void close() | 关闭此文件输出流并释放与此流相关联的任何系统资源。 |
void read() | 读数据 |
int by;
while ((by=fis.read())!=-1) {
System.out.print((char)by);
}
fis.close();
一次读一个字节数组的方法
byte[] bys = new byte[1024]; // 1024及其整数倍
int len;
while ((len=fis.read(bys))!=-1) {
System.out.print(new String(bys,0,len));
}
// 释放资源
fis.close();
字节缓冲流介绍
方法名 | 说明 |
---|---|
BufferedOutputStream(OutputStream out) | 创建字节缓冲输出流对象 |
BufferedInputStream(InputStream in) | 创建字节缓冲输入流对象 |
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\bos.txt"));
// 写数据
bos.write("hello\r\n".getBytes());
bos.write("world\r\n".getBytes());
// 释放资源
bos.close();
byte[] bys = new byte[1024];
int len;
while ((len=fis.read(bys))!=-1) {
fos.write(bys,0,len);
}
字符流的介绍
由于字节流操作中文不是特别的方便,所以Java就提供字符流
字符流 = 字节流 + 编码表
中文的字节存储方式
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
相关方法
方法名 | 说明 |
---|---|
byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节 |
String(byte[] bytes) | 使用平台的默认字符集解码指定的字节数组来创建字符串 |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来创建字符串 |
// 定义一个字符串
String s = "中国";
byte[] bys = s.getBytes(); //[-28, -72, -83, -27, -101, -67]
byte[] bys = s.getBytes("UTF-8"); //[-28, -72, -83, -27, -101, -67]
byte[] bys = s.getBytes("GBK"); //[-42, -48, -71, -6]
System.out.println(Arrays.toString(bys));
String ss = new String(bys);
String ss = new String(bys,"UTF-8");
String ss = new String(bys,"GBK");
System.out.println(ss);
字符流中和编码解码问题相关的两个类
InputStreamReader:是从字节流到字符流的桥梁
它读取字节,并使用指定的编码将其解码为字符
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
OutputStreamWriter:是从字符流到字节流的桥梁
是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
构造方法
方法名 | 说明 |
---|---|
InputStreamReader(InputStream in) | 使用默认字符编码创建InputStreamReader对象 |
InputStreamReader(InputStream in,String chatset) | 使用指定的字符编码创建InputStreamReader对象 |
OutputStreamWriter(OutputStream out) | 使用默认字符编码创建OutputStreamWriter对象 |
OutputStreamWriter(OutputStream out,String charset) | 使用指定的字符编码创建OutputStreamWriter对象 |
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"),"GBK");
osw.write("中国");
osw.close();
InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\osw.txt"),"GBK");
//一次读取一个字符数据
int ch;
while ((ch=isr.read())!=-1) {
System.out.print((char)ch);
}
isr.close();
方法介绍
方法名 | 说明 |
---|---|
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
刷新和关闭的方法
方法名 | 说明 |
---|---|
flush() | 刷新流,之后还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
方法介绍
方法名 | 说明 |
---|---|
int read() | 一次读一个字符数据 |
int read(char[] cbuf) | 一次读一个字符数组数据 |
字符缓冲流介绍
BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途
构造方法
方法名 | 说明 |
---|---|
BufferedWriter(Writer out) | 创建字符缓冲输出流对象 |
BufferedReader(Reader in) | 创建字符缓冲输入流对象 |
// BufferedWriter(Writer out)
// 根据数据源创建字符输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\bw.txt"));
bw.write("hello\r\n");
bw.write("world\r\n");
bw.close();
方法介绍
BufferedWriter:
方法名 | 说明 |
---|---|
void newLine() | 写一行行分隔符,行分隔符字符串由系统属性定义 |
BufferedReader:
方法名 | 说明 |
---|---|
String readLine() | 读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null |
// 写数据
for (int i = 0; i < 10; i++) {
bw.write("hello" + i);
//bw.write("\r\n");
bw.newLine();
bw.flush();
}
// 读数据
String line;
while ((line=br.readLine())!=null) {
System.out.println(line);
}
br.close();
// JDK9的改进方案
private static void method4() throws IOException {
FileReader fr = new FileReader("fr.txt");
FileWriter fw = new FileWriter("fw.txt");
try(fr;fw){
char[] chs = new char[1024];
int len;
while ((len = fr.read()) != -1) {
fw.write(chs, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
System类中有两个静态的成员变量
自己实现键盘录入数据
public class SystemInDemo {
public static void main(String[] args) throws IOException {
// public static final InputStream in:标准输入流
// InputStream is = System.in;
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);
// 自己实现键盘录入数据太麻烦了,所以Java就提供了一个类供我们使用
Scanner sc = new Scanner(System.in);
}
}
System类中有两个静态的成员变量
输出语句的本质:是一个标准的输出流
示例代码
public class SystemOutDemo {
public static void main(String[] args) {
// public static final PrintStream out:标准输出流
PrintStream ps = System.out;
//能够方便地打印各种数据值
// ps.print("hello");
// ps.print(100);
// ps.println("hello");
// ps.println(100);
// System.out的本质是一个字节输出流
System.out.println("hello");
System.out.println(100);
System.out.println();
// System.out.print();
}
}
字符打印流构造房方法
方法名 | 说明 |
---|---|
PrintWriter(String fileName) | 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新 |
PrintWriter(Writer out, boolean autoFlush) | 创建一个新的PrintWriter out:字符输出流 autoFlush: 一个布尔值,如果为真,则println , printf ,或format方法将刷新输出缓冲区 |
// 读写数据,复制文件, 不需要执行刷新
String line;
while ((line=br.readLine())!=null) {
pw.println(line);
}
对象序列化介绍
对象序列化流: ObjectOutputStream
构造方法
方法名 | 说明 |
---|---|
ObjectOutputStream(OutputStream out) | 创建一个写入指定的OutputStream的ObjectOutputStream |
序列化对象的方法
方法名 | 说明 |
---|---|
void writeObject(Object obj) | 将指定的对象写入ObjectOutputStream |
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
// 创建对象
Student s = new Student("林青霞",30);
// void writeObject(Object obj):将指定的对象写入ObjectOutputStream
oos.writeObject(s);
// 释放资源
oos.close();
注意事项
对象反序列化流: ObjectInputStream
构造方法
方法名 | 说明 |
---|---|
ObjectInputStream(InputStream in) | 创建从指定的InputStream读取的ObjectInputStream |
反序列化对象的方法
方法名 | 说明 |
---|---|
Object readObject() | 从ObjectInputStream读取一个对象 |
// ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
// Object readObject():从ObjectInputStream读取一个对象
Object obj = ois.readObject();
Student s = (Student) obj;
System.out.println(s.getName() + "," + s.getAge());
ois.close();
serialVersionUID
transient
public class Student implements Serializable {
private static final long serialVersionUID = 42L;
private String name;
// private int age;
private transient int age;
}
Properties介绍
Properties prop = new Properties();
// 存储元素
prop.put("itheima001", "林青霞");
// 遍历集合
Set<Object> keySet = prop.keySet();
for (Object key : keySet) {
Object value = prop.get(key);
System.out.println(key + "," + value);
}
特有方法
方法名 | 说明 |
---|---|
Object setProperty(String key, String value) | 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put |
String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
Set |
从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
// 创建集合对象
Properties prop = new Properties();
// 设置集合的键和值,都是String类型,底层调用Hashtable方法put
prop.setProperty("itheima001", "林青霞");
//Set<String> stringPropertyNames():从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
Set<String> names = prop.stringPropertyNames();
for (String key : names) {
// System.out.println(key);
String value = prop.getProperty(key);
System.out.println(key + "," + value);
}
和IO流结合的方法
方法名 | 说明 |
---|---|
void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
void store(OutputStream out, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流 |
void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流 |
private static void myLoad() throws IOException {
Properties prop = new Properties();
//void load(Reader reader):
FileReader fr = new FileReader("myOtherStream\\fw.txt");
prop.load(fr);
fr.close();
System.out.println(prop);
}
private static void myStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("itheima003","王祖贤");
//void store(Writer writer, String comments):
FileWriter fw = new FileWriter("myOtherStream\\fw.txt");
prop.store(fw,null);
fw.close();
}
进程:是正在运行的程序
是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序
方法介绍
方法名 | 说明 |
---|---|
void run() | 在线程开启后,此方法将被调用执行 |
void start() | 使此线程开始执行,Java虚拟机会调用run方法() |
实现步骤
public class MyThread extends Thread {
@Override
public void run() {
for(int i=0; i<100; i++) {
System.out.println(i);
}
}
}
public class MyThreadDemo {
public static void main(String[] args) {
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
// my1.run();
// my2.run();
// void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法
my1.start();
my2.start();
}
}
为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后由JVM调用此线程的run()方法
方法介绍
方法名 | 说明 |
---|---|
void setName(String name) | 将此线程的名称更改为等于参数name |
String getName() | 返回此线程的名称 |
Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
public class MyThread extends Thread {
public MyThread() {}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+":"+i);
}
}
}
my1.setName("高铁");
my2.setName("飞机");
// Thread(String name)
MyThread my1 = new MyThread("高铁");
MyThread my2 = new MyThread("飞机");
my1.start();
my2.start();
// 返回对当前正在执行的线程对象的引用
System.out.println(Thread.currentThread().getName());
线程调度
两种调度方式
Java使用的是抢占式调度模型
随机性
假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的
优先级相关方法
方法名 | 说明 |
---|---|
final int getPriority() | 返回此线程的优先级 |
final void setPriority(int newPriority) | 更改此线程的优先级 线程默认优先级是5;线程优先级的范围是:1-10 |
// public final int getPriority():返回此线程的优先级
System.out.println(tp1.getPriority()); //5
// 设置正确的优先级
tp1.setPriority(5);
相关方法
方法名 | 说明 |
---|---|
static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 |
void join() | 等待这个线程死亡 |
void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出 |
public class ThreadSleep extends Thread {
@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();
}
}
}
}
// join
tj1.start();
try {
tj1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//设置守护线程
td1.setDaemon(true);
td1.start();
Thread构造方法
方法名 | 说明 |
---|---|
Thread(Runnable target) | 分配一个新的Thread对象 |
Thread(Runnable target, String name) | 分配一个新的Thread对象 |
实现步骤
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0; i<100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class MyRunnableDemo {
public static void main(String[] args) {
//创建MyRunnable类的对象
MyRunnable my = new MyRunnable();
//创建Thread类的对象,把MyRunnable对象作为构造方法的参数
//Thread(Runnable target)
//Thread(Runnable target, String name)
Thread t1 = new Thread(my,"高铁");
Thread t2 = new Thread(my,"飞机");
//启动线程
t1.start();
t2.start();
}
}
多线程的实现方案有两种
相比继承Thread类,实现Runnable接口的好处
避免了Java单继承的局限性
适合多个相同程序的代码去处理同一个资源的情况(买票),把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
安全问题出现的条件
是多线程环境
有共享数据
有多条语句操作共享数据
如何解决多线程安全问题呢?
怎么实现呢?
把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
Java提供了同步代码块的方式来解决
同步代码块格式:
synchronized(任意对象) {
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
同步的好处和弊端
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
private int tickets = 100;
private Object obj = new Object();
@Override
public void run () {
while(true) {
// 同步代码块,异步同步化
synchronized (obj) {
// 进程进来后会锁住代码块
try {
t1.sleep(100);
}catch (InterruptedException) {
e.printStackTrace();
}
}
// 进程出来后会解锁
}
}
同步方法的格式
同步方法:就是把synchronized关键字加到方法上
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步方法的锁对象是什么呢?
this
静态同步方法
同步静态方法:就是把synchronized关键字加到静态方法上
修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步静态方法的锁对象是什么呢?
类名.class
@Override
public void run () {
while (true) {
sellTicket();
}
}
// 同步方法
private synchronized void sellTicket () {
// 进程进来后会锁住代码块
try {
t1.sleep(100);
}catch (InterruptedException) {
e.printStackTrace();
}
}
StringBuffer
线程安全,可变的字符序列
从版本JDK 5开始,被StringBuilder 替代。 通常应该使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步
Vector
Hashtable
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock构造方法
方法名 | 说明 |
---|---|
ReentrantLock() | 创建一个ReentrantLock的实例 |
加锁解锁方法
方法名 | 说明 |
---|---|
void lock() | 获得锁 |
void unlock() | 释放锁 |
private Lock lock = new ReentrantLock();
while (true) {
try {
lock.lock();
try {
// 执行
}
} finally {
lock.unlock();
}
}
概述
生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。
所谓生产者消费者问题,实际上主要是包含了两类线程:
一类是生产者线程用于生产数据
一类是消费者线程用于消费数据
为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为
消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为
Object类的等待和唤醒方法
方法名 | 说明 |
---|---|
void wait() | 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 |
void notify() | 唤醒正在等待对象监视器的单个线程 |
void notifyAll() | 唤醒正在等待对象监视器的所有线程 |
//提供存储牛奶和获取牛奶的操作
public synchronized void put(int milk) {
//如果有牛奶,等待消费
if(state) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有牛奶,就生产牛奶
this.milk = milk;
System.out.println("送奶工将第" + this.milk + "瓶奶放入奶箱");
//生产完毕之后,修改奶箱状态
state = true;
//唤醒其他等待的线程
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<=30; i++) {
b.put(i);
}
}
}
public class Customer implements Runnable {
private Box b;
public Customer(Box b) {
this.b = b;
}
@Override
public void run() {
while (true) {
b.get();
}
}
}
//创建奶箱对象,这是共享数据区域
Box b = new Box();
//创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
Producer p = new Producer(b);
//创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
Customer c = new Customer(b);
//创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
//启动线程
t1.start();
t2.start();
InetAddress:此类表示Internet协议(IP)地址
相关方法
方法名 | 说明 |
---|---|
static InetAddress getByName(String host) | 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址 |
String getHostName() | 获取此IP地址的主机名 |
String getHostAddress() | 返回文本显示中的IP地址字符串 |
InetAddress address = InetAddress.getByName("192.168.1.66");
// public String getHostName():获取此IP地址的主机名
String name = address.getHostName();
// public String getHostAddress():返回文本显示中的IP地址字符串
String ip = address.getHostAddress();
Java中的UDP通信
构造方法
方法名 | 说明 |
---|---|
DatagramSocket() | 创建数据报套接字并将其绑定到本机地址上的任何可用端口 |
DatagramPacket(byte[] buf,int len,InetAddress add,int port) | 创建数据包,发送长度为len的数据包到指定主机的指定端口 |
相关方法
方法名 | 说明 |
---|---|
void send(DatagramPacket p) | 发送数据报包 |
void close() | 关闭数据报套接字 |
void receive(DatagramPacket p) | 从此套接字接受数据报包 |
发送数据的步骤
DatagramSocket ds = new DatagramSocket();
// 数据打包
byte[] barr = "hello".getBytes();
DatagramPacket dp = new DatagramPacket(barr, barr.length(), InetAddres.getByName("192.168.1.1"), 10086);
// void send(DatagramPacket p) 从此套接字发送数据报包
ds.send(dp);
// void close() 关闭此数据报套接字
ds.close();
接收数据的步骤
构造方法
方法名 | 说明 |
---|---|
DatagramPacket(byte[] buf, int len) | 创建一个DatagramPacket用于接收长度为len的数据包 |
相关方法
方法名 | 说明 |
---|---|
byte[] getData() | 返回数据缓冲区 |
int getLength() | 返回要发送的数据的长度或接收的数据的长度 |
DatagramSocket ds = new DatagramSocket(10086);
while (true) {
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length());
// 调用DatagramSocket对象的方法接收数据
ds.receive(dp);
// 解析数据包,并把数据在控制台显示
System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
}
Java中的TCP通信
构造方法
方法名 | 说明 |
---|---|
Socket(InetAddress address,int port) | 创建流套接字并将其连接到指定IP指定端口号 |
Socket(String host, int port) | 创建流套接字并将其连接到指定主机上的指定端口号 |
相关方法
方法名 | 说明 |
---|---|
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
Socket s = new Socket("192.168.1.1", 10086);
// 获取输出流,写数据
// OutputStream getOutputStream() 返回此套接字的输出流
OutputStreams os = s.getOutputStream();
os.wirte("hello".getBytes());
// 释放资源
s.close();
构造方法
方法名 | 说明 |
---|---|
ServerSocket(int port) | 创建绑定到指定端口的服务器套接字 |
相关方法
方法名 | 说明 |
---|---|
Socket accept() | 监听要连接到此的套接字并接受它 |
// 创建绑定到指定端口的服务器套接字
ServerSocket ss = new ServerSocket(10086);
Socket s = ss.accept();
// 获取输入流,读数据,并把数据显示在控制台
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);
String data = new String(bys, 0, len);
sout(data);
// 释放资源
s.close();
ss.close();
案例分析
相关方法
方法名 | 说明 |
---|---|
void shutdownInput() | 将此套接字的输入流放置在“流的末尾” |
void shutdownOutput() | 禁止用此套接字的输出流 |
// 发送数据 -----------------------------
// 封装文本文件的数据
BufferedReader br = new BufferedReader(new FileReader("myNet\\InetAddressDemo.java"));
// 封装输出流写数据(字符缓冲)
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while ((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
bw.flush();
}
// public void shutdownOutput()
s.shutdownOutput();
// 接收数据 -----------------------------
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//把数据写入文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("myNet\\Copy.java"));
String line;
while ((line=br.readLine())!=null) { //等待读取数据
bw.write(line);
bw.newLine();
bw.flush();
}
案例需求
启动一个线程,在控制台输出一句话:多线程程序启动了
实现方式一
实现方式二
实现方式三
// 方式二
new Thread(new Runnable () {
@Override
public void run () {
sout("多线程");
}
}).start();
// 方式三
new Thread( () -> {
sout("多线程");
}).start();
函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”
而我们要学习的Lambda表达式就是函数式思想的体现
public interface Addable {
int add(int x,int y);
}
public class AddableDemo {
public static void main(String[] args) {
//在主方法中调用useAddable方法
useAddable((int x,int y) -> {
return x + y;
});
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
省略的规则
常量
public static final
抽象方法
public abstract
默认方法(Java 8)
静态方法(Java 8)
私有方法(Java 9)
格式
public default 返回值类型 方法名(参数列表) { }
范例
public default void show3() {
}
注意事项
默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
public可以省略,default不能省略
格式
public static 返回值类型 方法名(参数列表) { }
范例
public static void show() {
}
注意事项
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public可以省略,static不能省略
格式
public static 返回值类型 方法名(参数列表) { }
范例
public static void show() {
}
注意事项
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public可以省略,static不能省略
方法引用符
:: 该符号为引用运算符,而它所在的表达式被称为方法引用
推导与省略
引用类方法,其实就是引用类的静态方法
格式
类名::静态方法
范例
Integer::parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据
练习描述
定义一个接口(Converter),里面定义一个抽象方法 int convert(String s);
定义一个测试类(ConverterDemo),在测试类中提供两个方法
一个方法是:useConverter(Converter c)
一个方法是主方法,在主方法中调用useConverter方法
public interface Converter {
int convert(String s);
}
public class ConverterDemo {
public static void main(String[] args) {
//Lambda写法
useConverter(s -> Integer.parseInt(s));
//引用类方法
useConverter(Integer::parseInt);
}
private static void useConverter(Converter c) {
int number = c.convert("666");
System.out.println(number);
}
}
引用对象的实例方法,其实就引用类中的成员方法
格式
对象::成员方法
范例
“HelloWorld”::toUpperCase
String类中的方法:public String toUpperCase() 将此String所有字符转换为大写
public class PrintString {
//把字符串参数变成大写的数据,然后在控制台输出
public void printUpper(String s) {
String result = s.toUpperCase();
System.out.println(result);
}
}
// Lambda简化写法
usePrinter(s -> System.out.println(s.toUpperCase()));
// 引用对象的实例方法
PrintString ps = new PrintString();
usePrinter(ps::printUpper);
public interface MyString {
String mySubString(String s,int x,int y);
}
public static void main(String[] args) {
// Lambda简化写法
useMyString((s,x,y) -> s.substring(x,y));
// 引用类的实例方法
useMyString(String::substring);
}
private static void useMyString(MyString my) {
String s = my.mySubString("HelloWorld", 2, 5);
System.out.println(s);
}
public interface StudentBuilder {
Student build(String name,int age);
}
public class StudentDemo {
public static void main(String[] args) {
//Lambda简化写法
useStudentBuilder((name,age) -> new Student(name,age));
//引用构造器
useStudentBuilder(Student::new);
}
private static void useStudentBuilder(StudentBuilder sb) {
Student s = sb.build("林青霞", 30);
System.out.println(s.getName() + "," + s.getAge());
}
}
使用说明
Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数