目录
一.双列集合
1.Map
2.Map的遍历方式
3.可变参数
4.Collection中的默认方法
5.不可变集合(map不会)
二.Stream流
1.获取stream流
2.中间方法
3.stream流的收集操作
4.方法引用
1.引用静态方法
2.引用成员方法
3.引用构造方法
4.使用类名引用成员方法
5.引用数组的构造方法
三.异常
四.File
1.路径
2.方法
3.创建删除方法
4.获取并遍历
五.IO流
1.字节流
2.字符流
3.缓冲流
4.转换流
5.反序列化流/对象操作输入流
6.打印流
7.解压缩流/压缩流
8.压缩流
六.多线程
七.网络编程
每次插入两个数据
键 值对应 键值对
键不能重复,值可以重复
键+值被称为“键值对” 在Java中被称为"Entry对象"
是双列集合顶层接口,它的功能是全部双列集合都可以继承使用的
V put (K key,V value) 添加元素
在添加数据时,如果键不存在,那么直接把键值对对象添加到map集合中,方法返回null
在添加数据时,如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回
V remove(Object key)根据键删除键值对元素
void clear() 移除所有键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValu(Object () 集合的长度,也就是集合中键值对的个数
1.把键获取到单列集合里,再用get方法获取键对应的值 增强for 迭代器 lambda表达式
keySet();就是把所有键放到一个单列集合中
2.直接获取键值对对象
entrySet()方法返回一个set集合集合的泛型是Entry
3.Lambda表达式
底层是第二种遍历方式
HashMap特点
1.HashMap是Map里面的一个实现类。
2.没有额外需要学习的特有方法,直接用Map里面的方法就可以了。
3.特点都是由键决定的:无序、不重复、无索引
4.HashMap和HashSet底层原理是一模一样的,都是哈希表结构的
依赖hashCode方法和equals方法保证键的唯一性
如果存储的是自定义对象,需要重写hashCode和equals方法
如果值存储自定义对象,不需要重写hashCode和equals方法
static
static
static
方法形参的格式是可以发生变化的
本质上是一个数组
格式:属性类型...名字
int...args
在方法中,如果除了了可变参数以外,还有其他参数,那么可变参数要写在最后
批量添加元素
ArrayList
批量添加元素
Collections.addAll(list,"abc","sad");
打乱元素
Collections.shuffle(list);
不能被修改的集合
list.of
细节:
1.list直接用
2.set元素不能重复
3.map元素不能重复,键值对数量最多为10个
单列集合 default Stream
双列集合 无 一般先获取到单列集合再获取stream流
1.用keyset方法获取键的单列集合
2.用entryset方法获取键值对的单列集合
Stream流的三类方法
获取Stream流
创建一条流水线,并把数据放到流水线上准备进行操作
中间方法
流水线上的操作
一次操作完毕之后,还可以继续进行其他操作
终结方法
一个Stream流只能有一个终结方法
是流水线上的最后一个操作
数组 piblic static
一堆零散数据 piblic static
Stream
Stream
Stream
static
Stream
/原来的stream流只能使用一次,建议使用链式编程
void forEach(Consumer action) 对此流的每个元素执行操作
long count() 返回此流中的元素数
public static
public static
public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中
把已经有的方法拿过来用,当作函数式接口中抽象方法的方法体
1.引用处必须是函数式接口
1.被引用的方法必须已经存在
2.被引用的方法的形参和返回值需要与抽象方法保持一致。
3.被引用的方法的功能要满足当前需求
::方法引用符
格式 类名::静态方法 eg:Integer::parseInt
格式 对象::成员方法
其他类 其他类对象::方法名
本类 this::方法名
父类 super::方法名
格式 类名::new eg:Student::new
格式 类名::成员方法 eg:String::substring
第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法
在stream流中,第一个参数一般都表示流里面的每一个数据,假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法
格式:数据类型[]::new eg:int[]::new
异常是程序出现的问题
Exception:叫做异常,代表程序可能出现的问题,我们通常会用Exception以及它的子类来封装程序出现的问题。
运行时异常:RuntimeException及其子类,编译阶段不会出现异常提醒。运行时出现的异常
编译时异常:编译阶段就会出现的异常提醒。
异常的作用
1.异常是用来查看bug的参考信息
2. 异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况
处理方案
jvm默认处理方式
1.把异常的名称,原因,位置输出在控制台
2.程序停止了,下面的代码不会在执行
自己处理(捕获异常)
try{
可能出现异常的代码;
}catch(异常类名 变量名){
异常处理代码;
}
目的:出现异常时让程序继续执行
灵魂四问
灵魂一问:如果try中没有出项问题,怎么执行
把try中所有代码执行完毕,不会执行catch里面的代码
灵魂二问:如果try中可能遇到多个问题怎么执行
会写多个catch与之对应
细节:如果我们要捕获多个异常,这些异常中存在父子关系,那么父类一定要写在最下面
了解性:在jdk7以后,我们可以在catch中同时捕获多个异常,中间用隔开 表示如果出现A异常或B异常同时采用一种处理方案
灵魂三问:如果try中遇到的异常没有被捕获,怎么执行
相当于try...catch代码白写了,最终还是会交给虚拟机进行处理
灵魂四问:如果try中遇到了问题,那么try下面的代码会执行吗
不会执行了,直接跳到对应的catch中,执行catch里的语句体
Throwable的成员方法
String getMessage() 返回此throwable的详细消息字符串
String toString()返回可抛出的简短描述
void printStackTrace() 把异常的错误信息输出在控制台 底层用System.err.println把异常信息输出在控制台中
serr输出错误语句
抛出处理
throws:写在方法定义处,表示声明一个异常,告诉调用者使用本方法可能会有哪些异常
public void 方法() throws 异常类名1,异常类名2...{
...
}
编译时异常:必须要写。
运行时异常:可以不写。
throw:写在方法里面,结束方法 手动抛出异常对象,交给调用者 方法中下面的的代码不再执行了
public void 方法(){
throw new NullPointerException();
}
抛出:告诉调用者出错了
捕获:不让程序停止
自定义异常
1.定义异常
2.写继承关系
3.空参构造
4.带参构造
意义:为了让控制台的报错信息更加见名知意
继承关系:1.运行时:RuntimeException 2.编译时:Exception 核心:提醒程序员检查本地信息
throw和throws在Java中是两个不同的关键字,它们并不必须搭配使用。
throw用于在代码块中手动抛出一个异常,语法为throw new Exception()。
throws则用于方法声明中,表示该方法可能会抛出指定类型的异常,语法为public void methodName() throws Exception {}。
在方法中使用throw抛出异常时,并不需要在方法声明中使用throws声明该异常类型;反之,在方法声明中使用throws声明该异常类型时,并不要求方法内部一定要抛出该类型的异常。但是在实际开发中,为了更好地提示和管理异常,建议使用throws来声明方法可能会抛出的异常类型。
可变参数
getSum(int...args){
}
在方法的形参中最多只能写一个可变参数
在方法的形参中,如果除了可变参数以外还有其他形参,那么可变参数要写在最后
Collections
是集合的工具类
addAll(集合,对象,对象,对象,...) 批量添加数据
shuffle 打乱List集合元素的顺序
1.根据文件路径创建文件对象
public File(String pathname)
2.根据父路径名字字符串和子路径字符串创建文件对象
public File(String parent,String child)
3.根据父路径对应对应文件对象和子路径名字字符串创建文件对象
public File(File parent,String child)
isDirectory() 判断此路径名表示的File是否为文件夹
isFile() 判断此路径表示的File是否为文件
exists() 判断此路径名表示的File是否存在
length() 获取文件大小,但无法获取文件夹大小
getabsolutePath() 获取绝对路径
getPath() 返回定义文件时使用的路径
getName() 获取名字,文件调用返回文件名加后缀
文件夹调用返回文件夹名
lastModified() 返回文件最后修改时间(时间毫秒值)
createNewFile() 创建一个新的空的文件夹
mkdir() 创建单极文件夹
windows中路径是唯一的,如果当前路径已经存在,则创建失败,返回false
mkdir只能创建单极文件夹
mkdirs() 创建多级文件夹
既可以创建单极的又可以创建多级的
delete()
如果删除的是文件,则直接删除,不走回收站
如果删除的是空文件夹,则直接删除,不走回收站
如果删除的是有内容的回收站,则删除失败
File[] listFiles() 获取当前路径下所有内容
获取文件夹中所有内容,把所有内容放到数组中返回
1.当调用者File表示的路径不存在时,返回null
2.当调用者File表示的路径是文件时,返回null
3.当调用者File表示的路径是一个空文件夹时,返回一个长度为0的数组
4.当调用者File表示的路径是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
5.当调用者File表示的路径是一个有隐藏内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
6.当调用者File表示的路径是需要权限才能访问的文件夹时,返回null
存储和读取数据的解决方案
用于读写文件中的数据
纯文本文件
widows自带记事本打开能读懂的文件
FileOutputStream newfile = new FileOutputStream(文件名及路径);
newfile.write(int类型 );
newfile.close();
一.创建字节输出流对象
1.参数是字符串表示的路径或者是file对象都是可以的
2.如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
3.如果文件存在,则清空文件
二.写数据
write方法的参数是整数,但是实际上写到本地文件中的整数是整数在ASCII上对应的字符
三.释放资源
解除了资源的占用
write(int b) 一次写一个字节数据
write(byte[] b) 一次写一个字节数组数据
write(byte[] b,int of,int len) 一次写一个字节数组的一部分数据
str。getBytes()获取str字符串对应的byte数组
换行写:再次写出一个操作符就可以了 Windows:\r\n linux:\n mac:\r
续写:
如果想要续写,打开续写开关即可,开关位于对象的第二个参数默认为false:表示关闭续写,此时创建对象会清空文件 手动传递true:表示打开续写,此时创建对象不会清空文件
FileInputStream fis = new FileInputStream(文件名及路径);
fis.read ();
fis.close();
一.创建字节输入流对象
如果文件不存在,就直接报错
二.读取数据
1.一次读一个字节,读出来的是数据在ASCII上对应的数字
2.读到文件末尾了,read方法返回-1
三.释放资源
每次使用完流必须释放资源
循环读取
FileInputStream fis = new FileInputStream(文件名及路径);
int b;
while((b = fis.read()) != -1){
System.out.print((char)b);
}
fis.close();
有多个流时先开的流最后关闭
快速拷贝
int len = 0;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes,0,len);
}
fos.close();
fis.close();
为什么会有乱码
1.读取数据时未读完整个汉字
2.编码和解码方式不一致
如何不产生乱码
1.不要用字节流读取文本文件
2.编码解码时使用同一个码表,同一个编码方式
Java中编码的方法
getBytes() 使用默认方式进行编码
getBytes(String charsetName) 使用指定方式进行编码
Java中解码的方法
String(byte[] bytes) 使用默认方式进行解码
String(byte[] bytes,String charsetName) 使用指定方式进行解码
底层其实是字节流
特点
输入流:一次读一个字节,遇到中文时,一次读多个字节
输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中
使用场景
对于纯文本文件进行读写操作
FileReader fr = new FileReader(文件名及路径);
int ch = 0;
while ((ch = fr.read()) != -1) {(空参read方法)
System.out.print(ch);
}
fr.close();
1.read():默认也是一个字节一个字节读取的,如果遇到中文就会一次读取多个
2.在读取之后,方法底层还会进行解码并转成十进制,最终把这个十进制数据也表示在字符集上的数字
带参read方法:int read(char[] buffer)
字节流 可以拷贝任意类型的文件
字符流 1.读取纯文本文件中的数据
2.往纯文本文件中写出数据
基本流:
FileInputStream FileOutputStream
FileReader FileWriter
字节缓冲流
BufferedInputStream(基本流对象(,自己指定缓冲区大小))
BufferedOutputStream
字符缓冲输入流
BufferedReader
特有好用方法:readLine
字符缓冲输出流
BufferedWriter 读到末尾不返回-1而返回null
((line = br.readline()) != null)
特有好用方法:newline 跨平台换行
没有续写功能,写在构造的基本流里
io流随用随创建,随用随关闭
是字符流和字节流之间的桥梁
InputStreamReader
OutputStreamWriter
按照指定字符集进行读取数据
FileReader
InputStreamReader isr = new InputStreamReader(new FileInputStream(路径),"GBK");
int ch = 0;
while((ch = isr.read()) != -1){
System.out.print((char) ch);
}
isr.close();
序列化流/对象操作输出流
可以把Java中的对象写在本地文件中
ObjectOutputStream(OutputStream out) 把基本流包装成高级流
Serializable接口里面没有抽象方法,标记型接口
一旦实现了这个接口,那么就表示当前的Student类可以被序列化
理解:一个物品的合格证
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(路径));
per tsss = new per("涂山苏苏", 200);
oos.writeObject(tsss);
oos.close();
可以把序列化到本地文件中的对象,读取到程序中来
ObjectInputStream(InputStream in)
成员方法
Object readObject()
把系列化到本地文件中的对象,读取到程序中来
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(地址));
Object o = ois.readObject();
System.out.println((per) o);
ois.close();
transient:瞬态关键字
不会把当前属性序列化到本地文件中
不能读只能写 一般指PrintStream,PrintWriter两个类
打印流只能操作文件目的地,不操作数据源
特有的写出方法可以实现数据原样写出
特有的写出方法,可以实现自动刷新自动换行
write(int b) 规则跟之前一样,将指定字节写出
println(Xxx xx) 特有方法:打印任意数据,自动刷新自动换行
print(Xxx xx) 特有方法:打印任意数据,不换行
printf(String format,Object...args) 特有方法:带有占位符的打印语句,不换行
解压的本质:把压缩包中的每一个文件或文件夹读取出来,按照层级拷贝到目的地中
//创建一个解压缩流用来读取压缩包中的数据
ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
//要先获取到压缩包里面的每一个zipentry对象
//表示当前在压缩包中获取到的文件或者文件夹
ZipEntry entry;
while((entry = zip.getNextEntry()) != null){
System.out.println(entry);
if(entry.isDirectory()){
//文件夹:需要在目的地dest处创建一个同样的文件夹
File file = new File(dest,entry.toString());
file.mkdirs();
}else{
//文件:需要读取到压缩包中的文件,并把他存放到目的地dest文件夹中(按照层级目录进行存放)
FileOutputStream fos = new FileOutputStream(new File(dest,entry.toString()));
int b;
while((b = zip.read()) != -1){
//写到目的地
fos.write(b);
}
fos.close();
//表示在压缩包中的一个文件处理完毕了。
zip.closeEntry();
}
}
zip.close();
压缩本质:把每一个(文件/文件夹)看成ZipEntry对象放入压缩包中"D:\GAME\biji\js.txt""D:\GAME\biji\js.txt"
Commons-io工具包
是一组有关io操作的开源工具包
作用:提高io流开发效率
Hutool工具包
放评论区
并发:在同一时刻,有多个指令在单个CPU上交替进行
并行:在同一时刻,有多个指令在多个CPU上同时进行
多线程的实现方式
1.继承Thead类的方式进行实现
2.实现Runnable接口的方式进行实现
3.利用Callable接口和Future接口进行实现
第一种启动方式:
1.自己定义一个类继承Thead
2.重写run方法
3.调用start方法开启线程
第二种启动方式:
1.自己定义一个类实现Runnable接口
2.重写里面的run方法
3.创建自己的类的对象
4.创建一个Thread类的对象,并开启线程
Thread.currentThread()获取当前线程对象
第三种启动方式:
1.创建一个类MyCallable实现Callable接口
2.重写call(是有返回值的,表示多线程的运行结果)
3.创建MyCallable的对象(作用管理多线程运行的接口)
4.创建FutureTask的对象(作用管理多线程的运行结果)
5.创建thead类对象,并启动
实现Runnable、Callable接口
好处: 扩展性强,实现该接口的同时还可以继承其他的类
缺点: 编程相对复杂,不能直接使用Thread类中的方法
继承Thread类
好处: 编程比较简单,可以直接使用Thread类中的方法
缺点: 可以扩展性较差,不能再继承其他的类
常见的成员方法
void setName(String name) 将此线程的名称更改为等于参数name
如果没有给线程设置名字,线程也有默认名字 Thead-x(x序号,从0开始)
构造方法也可以设置名字 要在类里重写
String getName() 返回此线程的名称
Thread currentThread() 返回对当前正在执行的线程对象的引用
当JVM虚拟机启动之后,会自动的启动多条线程,其中有一条线程就叫做main线程,它的作用就是去调用main方法并执行里面的代码
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数
那条线程执行到这个方法,那么哪条线程就会在这里停留对应时间
线程优先级
两种调度方式
分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
Java使用的是抢占式调度模型
final int getPriority() 返回此线程的优先级
final void setPriority(int newPriority) 更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10
void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
public static void yield()出让线程/礼让线程
public final void join()插入线程
把调用此方法的线程插入到当前线程之前
线程的生命周期3
创建线程对象(新建)->有执行资格没有执行权(就绪,不停抢CPU)-><-有执行资格有执行权(运行,运行代码,直到执行权被抢走)->线程死亡,变成垃圾
如果阻塞了线程比如sleep就会回到就绪状态
线程安全问题
同步代码块
把操作数据的代码锁起来
synchronized(锁对象)<-锁对象什么都可以但要唯一{
操作共享数据的代码
}
锁默认打开,有一个线程进去了,锁自动关闭
里面的代码全部执行完毕,线程出来,锁自动打开
同步方法:
修饰符synchronized返回值类型 方法名(方法参数){...}
特点1:同步方法是锁住方法里面的所有代码
特点2:锁对象不能自己指定
非静态:this
静态:当前类的字节码文件对象
Lock锁
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock() 创建一个ReentrantLock的实例
void lock() 获得锁
void unlock() 释放锁
套路
1.循环
2.同步代码块
3.判断共享数据是否到了末尾(到了末尾)
4.判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)
用锁对象调用:
void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
void notify() 唤醒正在等待对象监视器的单个线程
void notifyAll() 唤醒正在等待对象监视器的所有线程
等待唤醒机制
生产者和消费者模式是一个十分经典的多线程协作模式
生产者:生产数据
消费者:消费数据
核心思想:用桌子来控制线程执行
阻塞队列方式实现
生产者类(Cooker):实现Runnable接口,重写run()方法,设置线程任务
1.构造方法中接收一个阻塞队列对象
2.在run方法中循环向阻塞队列中添加包子
3.打印添加结果
消费者类(Foodie):实现Runnable接口,重写run()方法,设置线程任务
1.构造方法中接收一个阻塞队列对象
2.在run方法中循环获取阻塞队列中的包子
3.打印获取结果
测试类(Demo):里面有main方法,main方法中的代码步骤如下
创建阻塞队列对象
创建生产者线程和消费者线程对象,构造方法中传入阻塞队列对象
分别开启两个线程
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。
1.创建一个池子
2.提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再提交任务时,不需要创建新的线程,直接复用已有线程即可
3.但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
步骤:
1.创建线程池
2.提交任务
3.所有任务全部执行完毕后,关闭线程池
static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池(上限时int的最大值)
static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池
参数一:核心线程数量
参数二:最大线程数
参数三:空闲线程最大存活时间
参数四:时间单位
参数五:任务队列
参数六:创建线程工厂
参数七:任务的拒绝策略
cpu密集型运算 最大并行数+1
I/O密集型运算
在网络通信协议下,不同计算机上运行的程序,进行数据传输
常见软件架构
C/S:Client/Server
客户端/服务器
在用户本地需要下载并安装客户端程序,在远程有一个服务端程序。
画面可以做的精美,用户体验好
需要开发客户端,也需要开发服务端
用户需要下载更新太麻烦
B/S:Browser/Server
浏览器/服务器
只需要一个浏览器,用户通过不同的网址,客户访问不同的服务器
优缺点:
不需要开发客户端,只需要页面+服务端
用户不需要下载,打开浏览器就能使用
如果应用过大,用户体验受到影响
网络编程三要素
确定对方电脑在互联网上的地址(IP)
IP:设备在网络中的地址,是唯一标识
确定接收数据的软件(端口号)
端口号:应用程序在设备中唯一的标识
确定网络传输的规则(协议)
协议:数据在网络中传输的规则,常见的协议有UDP TCP Http https ftp
IP
IPv4
互联网通信协议第四版
采用32位地址,用点分十进制表示法
IPv6
IPv4不够用了,只有42亿个在2019年分配完毕
于是有了IPv6采用128位地址长度,分为8组
用冒分16进制表示法
特殊IP地址
127.0.0.1,也可以是localhost:是回送地址,也称本机IP,永远只会寻找当前所在本机
自己写练习用127.0.0.1
常用cmd命令
ipconfig:查看本机IP地址
ping ip地址:检查网络是否畅通
InetAddress
没有对外提供构造方法,需要用getByName()方法获取对象
一台电脑的对象
端口号
两个字节表示的整数,取值范围0~65535
0~1023用于一些知名的网络服务或者应用
一个端口号只能一个应用使用
tcp/ip参考模型
应用层 传输层 网络层 物理链路层
UDP协议
用户数据报协议
面向无连接通信协议
速度快,有大小限制一次最多发送64k,数据不安全,易丢失数据
TCP协议
传输控制协议TCP
面向连接的通信协议
速度慢,没有大小限制,数据安全
UDP
1.创建DatagramSocket对象(飞机)
细节:绑定端口 以后我们就是通过这个端口往外发送数据
空参:所有可用端口中随机一个进行使用
有参:指定端口号进行绑定
2.打包数据
3.发送数据
4.释放资源
DatagramSocket ds = new DatagramSocket();(创一个飞机对象)
String str = "wohaoshuai";(要送的东西)
byte[] b = str.getBytes();(贴一层保护膜)
DatagramPacket dp = new DatagramPacket(b,b.length,InetAddress.getByName("127.0.0.1"),10086);(装上箱子)
ds.send(dp);(飞机送过去)
ds.close();(飞机熄火)
接收数据
1.创建DatagramSocket对象
细节:
绑定端口,以后我们就是通过这个端口往外发送
而且绑定的端口和一定要和发送的端口保持一致
2.接收数据包
3.解析数据包
4.释放资源
UDP三种通信方式
1.单播
一对一
2.组播
一对组
创建MulticastSocket对象
组播地址:224.0.0.0~239.255.255.255
剩下一样
其中224.0.0.0~224.0.0.255为预留的组播地址
3.广播
广播地址
只需要把获取地址改成255.255.255.255就行了
TCP通信协议
是一种可靠的网络协议,他在通信两端各建立一个Socket对象
通信之前要保证连接已经建立
通过Socket产生的IO流来进行网络通信
客户端
1.创建客户端的Socket对象(Socket)与指定的服务端连接
2.获取输出流,写数据
3.释放资源
服务器
1.创建服务器端的Socket对象
在创建对象的同时会连接服务端,如果连接不上,代码会报错
2.监听客户端连接,返回一个Socket对象,就是等客户端连接
3.获取一个输入流,读数据,并把数据显示在控制台上
4.释放资源
反射
反射允许对封装类的字段,方法和构造函数的信息进行编程访问
获取class对象的三种方法
1.Class.forname("全类名");(最常用)
2.类名.class(一般当作参数使用)
3.对象.getClass()(当我们已经有了这个类的对象时)
第一种方式
全类名:包名+类名
Constructor>[] getConstructors() 获得所有的构造(只能public修饰)
Constructor>[] getDeclaredConstructors() 获得所有的构造(包含private修饰)
Constructor
Constructor
getModifiers() 以整数形式返回此 Constructor 对象所表示构造方法的 权限修饰符。
getParameterTypes()
按照声明顺序返回一组 Class 对象,这反射获取些对象表示此 Constructor 对象所表示构造方法的形参类型。
利用反射获取成员变量
Field
在字节码Class文件中直接getxxx方法获取
获取类中成员变量信息
getName() 返回此 Field对象表示的字段的名称
getModifiers() 返回权限修饰符
getType() 返回字段类型
Method
getModifiers() 以整数形式返回修饰符
getName() 返回方法名称
getExceptionTypes() 返回底层方法抛出的异常类型。
invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。就是调用方法
反射的作用
1.获取一个类中的所有信息,获取到以后,再执行其他业务逻辑
2.结合配置文件,动态的创建对象并调用方法
动态代理
无侵入式的给代码增加额外功能