File
- 文件和目录通过File封装成对象,可以直接通过代码对文件进行操作:创建,删除等
- File的三种构造方法(其中路径名有两种形式:
/
或者\\
)
构造名 | 描述 |
---|---|
File(String pathname) | 传递的参数是文件夹/文件的地址 |
File(String parent,String parent) | 传递的参数是父路径和文件夹的拼接 |
File(File parent,String child) | 先获取父路径的file名,再拿到文件夹名 |
- 对file文件夹的操作
操作方法 | 描述 |
---|---|
createNewFile() | 创建新的文件 |
mkdir() | 创建目录 |
mkdirs() | 创建多层目录 |
exists() | 文件/文件夹是否存在 |
isFile() | 是否是一个文件 |
isDirectory() | 是否是一个文件夹 |
getAbsolutePath() | 获取文件/文件夹的绝对路径 |
getName() | 在路径中获取文件/文件夹的名字 |
list() | 获取file中所有的文件名 |
listFiles() | 获取目录下所有的文件/文件夹 |
delete() | 删除文件/文件夹(但是里面有内容是无法删除的,所以需要使用递归) |
file.createNewFile()
:创建文件的两种方式
1)直接在获取文件对象时,创建文件
//创建文件的第一种方式
File file = new File("C:\\Users\\lenovo\\Desktop\\test\\test1.txt");
if (!file.exists()){
file.createNewFile();
}
2)使用创建文件对象的第三种方式时,创建文件
//创建文件的第二种方式
File file1 = new File("C:\\Users\\lenovo\\Desktop\\test");
File file2 = new File(file1, "test2.txt");
file2.createNewFile();
file.mkdir()
:创建文件的三种方式
1)直接在创建文件对象后面添加
//第一种创建文件夹的方式
File file = new File("C:\\Users\\lenovo\\Desktop\\test\\testli");
if (!file.exists()){
file.mkdir();
}
2)使用创建文件的第三种方式的时候,创建文件夹
//第二种创建文件夹的方式
File file1 = new File("C:\\Users\\lenovo\\Desktop\\test");
File testli1 = new File(file1, "testli1");
if (!testli1.exists()){
testli1.mkdir();
}
3)创建多层文件夹的方式
//创建多层文件夹的方式‘
File file2 = new File("C:\\Users\\lenovo\\Desktop\\test\\testli\\testlili");
if(file2.exists()){
file2.mkdirs();
}
- 获取文件的路径
1)file.getAbsoultePath()
:获取文件的绝对路径
2)file.getName()
:获取路径中的最后一位文件名(当3)中使用getName获取的是test)
File file = new File("C:\\Users\\lenovo\\Desktop\\test");
File file1 = new File(file, "\\test78\\test56");
if (!file1.exists()){
file1.mkdirs();
}
System.out.println("获取文件绝对路径"+file1.getAbsolutePath());
System.out.println("获取文件/文件夹名"+file1.getName());
3)file.list()
:获取目录下所有的文件夹名
4)file.listFile()
:获取目录中的所有的文件
public static void main(String[] args) {
//获取test目录下所有的文件夹/文件
File file = new File("C:\\Users\\lenovo\\Desktop\\test");
File[] files = file.listFiles();
System.out.println(Arrays.toString(files));
}
file.delete()
:因为delete只能删除里面没有数据的文件,所以可以使用递归删除有数据的文件
public static void main(String[] args) {
File file = new File("C:\\Users\\lenovo\\Desktop\\test");
getAllFiles(file);
}
public static void getAllFiles(File file){
//用listFiles获取所有的文件和文件夹
File[] files = file.listFiles();
if (files!=null){
//遍历所有的文件
for (File dataFile:files) {
//当是文件夹的时候会删除
if(dataFile.isDirectory()){
getAllFiles(dataFile);
dataFile.delete();
}
}
}else{
file.delete();
}
}
- 递归的前提条件:
- 递归必须放到方法中
- 递归必须有结束,return
- 在最终结果为1时,结束递归
- 手写斐波那列契(不死神兔)
IO流
- 说说你知道的流?字节流,字符流,字节缓冲流,字符缓冲流,转换流(将字节转为字符),字节打印流,字符打印流,序列化流,反序列化流
- IO流是输入输出流,可以通过流实现数据在设备间的传输,常见的有文件的上传,下载,复制
- IO流的分类
- 按照数据的流向
- 输入流:读数据(Input)
- 输出流:写数据(Ouput)
- 按照数据类型来分
- 字节流
- 字节输入流
- 字节输出流
- 字符流
- 字符输入流
- 字符输出流
- 字节流
- IO流分为字符流和字节流:
- 操作文本文件,使用字符流
- 操作图片,视频,音频等二进制文件,使用字节流
- 对于不确定类型的,使用万能的字节流(二进制)
- 字节输出流
- 字节输出流是
FileOutputStram
,可以用来写入数据write()
,- 写入数据有三种方式,分别是
write()
的三种重载方法write(字节数组,0,长度)
- 字节输出流每次默认自动覆盖上次的内容,在创建字节输出流的时候,将第二次参数置为
true
,就可以实现上次内容的拼接(new FileOutputStream(PathName,true)
)- 字节输出流写入数据的步骤:
1)
创建字节输出流对象,指向好写入的文件名2)
调用对象的write()方法3)
关闭字节输出流
public static void main(String[] args) {
//创建文件,因为要通过IO流,往里面写数据
File file = new File("C:\\Users\\lenovo\\Desktop\\test");
File file1 = new File(file, "test.txt");
if (!file1.exists()){
try {
file1.createNewFile();
} catch (Exception e) {
e.printStackTrace();
}
}
try {
//创建输出流,写入数据
FileOutputStream outputStream = new FileOutputStream(file1);
String data="咱们老百姓啊,今个真高兴";
//write只能存储byte,int类型的,所以要转一下
//outputStream.write(data.getBytes());
outputStream.write(data.getBytes(),0,data.getBytes.length)
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
- 字节输入流
- 字节输入流是
FileInputStream
,用于从文件中读取数据通过方法read()
- 读取数据也有三种方式,分别是
read()
的三种重载方法,其中有个read(字节数组,0,长度)
- 读取数据时,字节长度不能太长,否则会撑破管道,造成数据丢失,所以我们一般定义1024个字节(
byte[] bytes=new byte[1024]
)- 字节输入流读取数据的步骤:
1)
创建字节输入流,指定好读入数据的文件名2)
调用对象的read()方法3)
关闭管道
public static void main(String[] args) {
//从那个文件中读取数据
File file = new File("C:\\Users\\lenovo\\Desktop\\test\\test.txt");
FileInputStream inputStream=null;
try {
//做个管道流
inputStream = new FileInputStream(file);
//怕流管道承受不住,一次只传输1024个字节
byte[] bytes = new byte[1024];
//当管道中有值时
while (inputStream.read(bytes)!=-1){
//将字符数组转为字符串String
//每次数据转换的时候,从0开始到1024个长度结束,为了防止数据丢失
String s = new String(bytes,0,inputStream.read(bytes));
System.out.println(s);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 字节输入输出流,实现文件的复制(读一行写一行,不能全部读入内存)
public static void main(String[] args) {
//输出流,写入数据
FileInputStream inputStream=null;
//输入流,读取数据
FileOutputStream fileOutputStream=null;
try {
inputStream = new FileInputStream("C:\\Users\\lenovo\\Desktop\\test\\smq\\test.txt");
fileOutputStream = new FileOutputStream("C:\\Users\\lenovo\\Desktop\\test\\xue\\test.txt");
//字节数组不能太长,数据会撑破流,造成数据丢失
byte[] bytes = new byte[1024];
int len=0;
while ((len=inputStream.read(bytes))!=-1){
fileOutputStream.write(bytes,0,len);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 字节输入输出流,实现图片的复制(和文件的复制是一样的)
public static void main(String[] args) {
try {
//输出流,是写入
FileOutputStream outputStream = new FileOutputStream("C:\\Users\\lenovo\\Desktop\\test\\xue\\test.png");
//输入流,读取出来
FileInputStream inputStream = new FileInputStream("C:\\Users\\lenovo\\Desktop\\test\\smq\\test.png");
byte[] bytes = new byte[1024];
int len=0;
while ((len=inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,len);
}
outputStream.close();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
- 在使用bufferOutputStream的时候一定要刷新流
对象.flush()
,否则数据出不来(写不进去) - 字符串中编码解码的问题
- 字符串解码转为字节数组:
getBytes("GBK/UTF8")
- 字节数组编码转为字符串:
new String(字符数组,“GBK/UTF8”)
String str="你好";
//将字符串转为GBK字符集,GBK存储汉字是2个
byte[] gbks = str.getBytes("GBK");
System.out.println("GBK字符集"+ Arrays.toString(gbks));
//将字符串转为UTF-8字符集,UTF8存储汉字是3个
byte[] utf8s = str.getBytes("UTF8");
System.out.println("UTF8字符集"+Arrays.toString(utf8s));
//将GBK字符集转回为字符串
String sgbk = new String(gbks,"GBK");
System.out.println("GBK转为字符集"+sgbk);
//将UTF8转回为字符集
String sutf8 = new String(utf8s);
System.out.println("UTF8转为字符集"+sutf8);
- 字符流之转换流(InputStreamReader)和字节流搭配使用,流嵌套使用时必须要刷新(可以将字节转为字符流,做文本数据传输)
public static void main(String[] args) throws IOException {
//创建字符流之转换流,读出
FileInputStream fis = new FileInputStream("C:\\Users\\lenovo\\Desktop\\xiyouji\\swk.txt");
java.io.InputStreamReader gbkInput = new java.io.InputStreamReader(fis, "GBK");
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\lenovo\\Desktop\\xiyouji\\smk798.txt");
OutputStreamWriter gbkOutput = new OutputStreamWriter(fileOutputStream, "GBK");
char[] chars = new char[1024];
while (gbkInput.read(chars)!=-1){
gbkOutput.write(chars);
//关闭流
gbkOutput.flush();
}
}
- 字符流(FileReader)和缓冲流(bufferReader),使用缓冲流的目的是防止字符流管道被撑破,造成数据的丢失
readLine()
是按行读取,是BufferReader的方法
public static void main(String[] args) throws IOException {
//字符输入流,读取数据
FileReader fileReader = new FileReader("C:\\Users\\lenovo\\Desktop\\xiyouji\\swk.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
//字符输出流,写入数据
FileWriter fileWriter = new FileWriter("C:\\Users\\lenovo\\Desktop\\xiyouji\\swk123456.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
//字符是char,字节使用的是byte
char[] chars = new char[1024];
String len=null;
//使用readLine是一行一行读取数据,但是写出是全部写出
while ((len=bufferedReader.readLine())!=null){
bufferedWriter.write(len);
//嵌套的流必须用刷新
bufferedWriter.flush();
}
}
- 你还知道什么流?序列化对象流,反序列化对象流,字节打印流,字符打印流
多线程
- 进程:一个程序运行起来就叫进程
- 线程:程序运行的时候的执行路径
- 单线程:只有一个主线程,顺序执行,线程安全,
- 多线程:主线程和子线程同时执行,但是主线程和子线程一定有一个先输出,谁先抢占到CPU时间片,就会先输出哪个线程,多线程会节约时间,但是同时操作的数据会不安全
- 同步和异步的区别
- 同步的是单线程,按顺序执行
- 异步的是多线程,同一时刻可以操作多条线程
- 并发与并行的区别:
- 并发是一个处理器处理多个任务,多个任务在同一时间段内发生
- 并行是多个处理器处理多个任务,两个或多个任务在同一时刻发生
- 什么是死锁:
- 使用wait()使线程等待,不用nodify来解锁
- sycnified里面嵌套,(两个线程相互等待释放资源)
- 线程死锁:加锁别的线程无法访问数据,死锁如何造成的:wait()等待,synchronized同步代码块嵌套定义,Lock lock=new ReentrantLock,一旦使用lock.lock没有lock.unlock这几种会造成死锁
- 实现多线程的两/三种方式:
- 子线程中
继承thread
类,重写run()
方法;在主线程中调用start()
方法开启线程- 子线程
实现Runnable
类,重写run()
方法;创建子线程对象,创建Thread
对象,把子线程对象作为构造方法的参数- 还有一种匿名内部类,不经常使用(是2的进阶)
- 线程中的方法
方法 | 描述 |
---|---|
start() | 开启线程 |
run() | 线程要执行的代码 |
setName() | 设置线程的名字 |
currentThread() | 当前的线程名 |
Thread.currentThread.getName() | 获取当前的线程名 |
getPriority() | 获取线程的优先级 |
setPriority(6) | 设置线程的优先级 |
Thread.sleep(毫秒) | 线程阻塞睡眠 |
join() | 线程阻塞 |
setDaemon(true) | 设置为守护线程 |
1)实现Runable的子线程
public class RunDemom implements Runnable{
@Override
public void run() {
System.out.println("子线程");
}
}
2)实现Runable的主线程
public static void main(String[] args) {
RunDemom runDemom = new RunDemom();
Thread thread = new Thread(runDemom);
//直接调用runDemom的start是不可以的,因为start是Thread,而RunDemom是直接实现Runable的,没有start的
//开启线程
thread.start();
}
1)继承thread的子线程:
public class ZiThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我是子线程"+i);
}
}
}
2)继承thread的主线程:
public class ZhuThread {
public static void main(String[] args) {
ZiThread ziThread = new ZiThread();
ziThread.start();
for (int i = 0; i < 100; i++) {
System.out.println("我是主线程"+i);
}
}
}
1)匿名内部类
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程");
}
}).start();
}
- 为什么重写run方法:run方法才是线程执行的内容
- run方法和start方法的区别:run是线程执行的代码可以直接调用,start启动线程,然后JVM调用此线程的run()
- 关于线程名的方法:setName,getName,currentThread(
Thread.currentThread.getName()
获取当前的线程名) - 设置线程的优先级:
getPriority()
获取当前的优先级(默认是5,最高是10),setPriority(10)
设置当前的优先级
1)子线程
public class ThreadDemo1 extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
2)主线程
public static void main(String[] args) {
ThreadDemo1 demo1 = new ThreadDemo1();
//设置上名字,必须要获取,否则也取不到名字
demo1.setName("我是线程的名字");
demo1.start();
//设置优先级
demo1.setPriority(10);
}
- 线程的阻塞方式有三种:
Thread.sleep(1000);
过了这个时间,该线程才可以被调用,对象名.join()
等待当前线程的执行完毕,才可以执行下一个线程(开启线程时strat会立即执行这个join);wait()
1)子线程(只在子线程上加入Thread.sleep()
,父线程还是原来的)
public class ThreadDemo1 extends Thread{
@Override
public void run() {
Thread.sleep(2500);
System.out.println(Thread.currentThread().getName());
}
}
-
线程的生命周期
-
多线程中可能会有操作同一个数据,数据不安全的问题,必须使用
implement Runable
,相同的程序去处理同一个资源数据,很好的体现了面向对象的思想
- 方式一:使用同步代码块锁,一个线程操作完数据,另一个线程才继续操作数据,(把多条语句操作共享数据的代码锁起来)
- 格式:
synchronized(任意对象){}
- 方式二:使用同步方法,把
synchronized
加到方法中- 方式三:使用lock加锁,哪个方法操作数据就可以在哪加锁
Lock lock = new ReentrantLock();
//上锁
lock.lock();
//上锁之后,必须要解锁
lock.unlock();
- 同步代码块和同步方法哪个更加好用?
- 同步代码块直接写在run()方法中
- 同步方法是单独写成方法,在run方法中调用同步方法
网络编程
- 网络通信协议:计算机中连接通信的协议,有TCP传输控制协议和UDP用户数据报协议两种
- UDP是无连接通信协议,不会确定接收端是否存在,可能会有数据丢失的情况,但是发送端和接收端会有socket对象,用于接收数据报,通信效率高,用于传输不重要的数据
- TCP有连接通信协议,客户端和服务端必须先建立连接三次握手,断开连接四次挥手
- 三次握手:
- 发送端发送请求连接的消息
- 接受端确认消息,表示收到
- 发送端再次发送数据,代表握手结束
- 四次挥手
- 客户端关闭客户到服务端的连接
- 服务器端ACK确认
- 服务器端关闭服务器到客户端的连接
- 客户端ACK确认
- UDP发送报文数据:
- 先创建UDP的套接字
- 写入要发送的数据
- 将第二步的数据转为字节
getBytes()
- 创建报文对象,将字节数组传到报文的构造方法中
- 使用套接字的
send()
方法发送报文
接收方:
public static void main(String[] args) throws Exception {
System.out.println("请输入数据");
//1.创建UDP的socket套接字
DatagramSocket socket = new DatagramSocket();
//2.发送的数据
Scanner scanner = new Scanner(System.in);
while (true){
String next = scanner.nextLine();
if (next.equals("null")){
socket.close();
break;
}else{
//3.将发送的数据转为字节,才传输
byte[] bytes = next.getBytes();
//4.将数据封装到报文对象中(直接放地址是不可以的,要转为InetAddress)
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length, InetAddress.getByName("127.0.0.1"),65533);
//5.将报文装到套接字中发送
socket.send(packet);
}
}
}
- UDP接收报文数据
- 创建UDP中的套接字
- 创建字节数组,因为发送的是字节数组,所以要用字节数组接收
- 创建报文对象
- 用套接字
receive()
接收报文对象
//1.创建UDP的套接字
DatagramSocket socket = new DatagramSocket(65533);
byte[] bytes = new byte[1024];
//2.接收报文对象,一直开着接收就行了
while (true) {
DatagramPacket packe13 = new DatagramPacket(bytes, 0, bytes.length);
//3.用套接字接收报文对象
socket.receive(packe13);
System.out.println("接收方接收的数据:"+new String(packe13.getData()));
}
- 利用多线程实现相互传递 消息
子线程
public class ThreadReceive implements Runnable{
@Override
public void run() {
//1.创建UDP的套接字
DatagramSocket socket = null;
try {
socket = new DatagramSocket(65533);
} catch (SocketException e) {
e.printStackTrace();
}
byte[] bytes = new byte[1024];
//输出数据
Scanner scanner = new Scanner(System.in);
//2.接收报文对象,一直开着接收就行了
while (true) {
DatagramPacket packe13 = new DatagramPacket(bytes, 0, bytes.length);
//3.用套接字接收报文对象
try {
socket.receive(packe13);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("小白:"+new String(packe13.getData()));
//===================4.接收方发送数据==============================================
String next = scanner.next();
if (next.equals("null")){
break;
}else{
byte[] bytes1 = next.getBytes();
DatagramPacket packet = null;
try {
packet = new DatagramPacket(bytes1, 0, bytes1.length, InetAddress.getByName("127.0.0.1"), 8888);
} catch (UnknownHostException e) {
e.printStackTrace();
}
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
主线程
public class ThreadSend {
public static void main(String[] args) throws IOException {
ThreadReceive receive = new ThreadReceive();
Thread thread = new Thread(receive);
thread.start();
sendDemo();
}
public static void sendDemo() throws IOException {
System.out.println("请输入数据");
//1.创建UDP的socket套接字
DatagramSocket socket = new DatagramSocket(8888);
// DatagramSocket socket = new DatagramSocket(65533);
//2.发送的数据
Scanner scanner = new Scanner(System.in);
while (true){
String next = scanner.next();
if (next.equals("null")){
socket.close();
break;
}else{
//3.将发送的数据转为字节,才传输
byte[] bytes = next.getBytes();
//4.将数据封装到报文对象中(直接放地址是不可以的,要转为InetAddress)
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length, InetAddress.getByName("127.0.0.1"),65533);
//5.将报文装到套接字中发送
socket.send(packet);
//====================接收方发送数据======================================================================
byte[] bytes1 = new byte[1024];
DatagramPacket packet1 = new DatagramPacket(bytes1, 0, bytes1.length);
socket.receive(packet1);
System.out.println("小明:"+new String(packet1.getData()));
}
}
}
}
- TCP发送方发送数据,使用Socket
Scanner scanner = new Scanner(System.in);
//1.创建发送方的套接字
Socket socket = new Socket("127.0.0.1",65530);
while (true){
//1.套接字的输出流
OutputStream outputStream = socket.getOutputStream();
String next = scanner.next();
//2.数据字节输出
outputStream.write(next.getBytes());
}
- TCP接收方接收数据,使用ServerSocket
//1.接收方套接字
ServerSocket serverSocket = new ServerSocket(65530);
//2.接收套接字的数据
Socket socket = serverSocket.accept();
while (true){
//3.拿到输入流
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
//4.输入流读取数据
inputStream.read(bytes, 0, bytes.length);
System.out.println("接收方接收数据"+new String(bytes));
}
类加载
- 类加载器:因为JVM中有类加载器,(Java中内置类加载器)才可以执行class文件
- 类加载器的作用:ClassLoade类,这个类是JVM使用的,Java文件可以通过类加载器直接和class字节码文件使用
反射
- 对于一些很特殊的类,是不能实例化调用的,就能使用反射
- 反射:不实例化对象,可以直接在代码中使用类中的属性和方法(通过拿到class文件,得到Java文件),这样的好处是提高了代码的灵活性和可扩展性
- 那不实例化,该如何调用对象呢?(获取class对象)
- 类名.class
- 对象名.getClass()(但是这种方式也要实例化??)
- Class.forName(”包名+类名")(包名+类名也叫全限定类名)
- 不实例化,拿到类中的构造方法(通过class拿到构造方法)
getConstructors()
拿到所有的公共的构造方法getDeclaredConstructors()
拿到所有的包括私有的构造方法getConstructor(String.class)
拿到单个的公共的构造方法getDeclaredConstructor(int.class)
拿到单个的包括私有的构造方法
- 获得构造方法是为了实例化对象
构造方法名.newInstance()
可以拿到对象(通过构造方法拿到对象引用)
public static void main(String[] args) throws Exception {
//以前是实例化,构造方法,通过编辑器拿到字节码文件,如下的步骤是完全反了
//1. 获取class字节码对象
Class<?> aClass = Class.forName("fanshe.User");
//2. 拿到class对象的构造方法
Constructor<?> constructor = aClass.getConstructor();
//3. 通过构造方法拿到对象
Object o = constructor.newInstance();
System.out.println(o);
}
- 在不知道类的情况下,拿到属性和方法
反射获取属性 | 描述 |
---|---|
字节码对象.getFields() | 获取公共的属性 |
字节码对象.getDeclaredFields() | 获取所有包括私有的属性 |
字节码对象.getMethods() | 获取公共的方法 |
字节码对象.getDeclaredMethods() | 获取所有包括私有的方法 |
字节码对象.getDeclaredMethod(“方法名”) | 通过方法名获取方法,返回值是方法 |
-
拿到属性和方法的目的是在不知道方法名的情况下使用:
方法名.invoke(对象名)
反射调用类中的方法 -
暴力调用方法或者属性:
方法名.setAccessible(true)
因为方法是私有的