Java基础
1.面向对象
1.1 三大特性
- 封装: 类
- 继承
- 多态:一个行为多个表现
实现:1. 接口实现 2. 继承父类重写方法 3. 同一类中进行方法重载
1.2 重载和重写
- 重载:一个类中方法名相同
条件:1.方法名相同 2.参数列表不同(个数 类型 顺序) 3.返回类型 (仅返回类型不同不足以) 4.发生在编译期间 - 重写:子类与父类 @Override
静态方法不能被重写
1.3 初始化顺序
静态属性 静态方法块 普通属性 普通方法 构造器
public class Order1 {
{
System.out.println("Order1 {}");
}
static {
System.out.println("Order1 Static");
}
Order1(){
System.out.println("Oder1");
}
}
public class Order extends Order1{
{
System.out.println("Order {}");
}
static {
System.out.println("Order Static");
}
Order(){
System.out.println("Oder");
}
public static void main(String[] args) {
Order order = new Order();
}
}
- 运行结果
1.4 访问控制权限
5.转型
- 向上转型:子类到父类 不需要强制
- 向下转型:父类到子类 需要强制
6.static
静态变量存储在方法区,属于类所有。实例变量存储在堆当中,其引用存在当前线程栈。
7.final
- 修饰类,无法被继承
- 修饰方法: 无法被重写
- 修饰变量:基本类型值无法更改 引用类型无法更改引用
8.抽象类abstract
- 抽象方法一定是抽象类,抽象类不一定有抽象方法
- 实现与否都可以
- 无法实例化
1.8.1 和接口对比
- 默认方法
抽象类可以有默认的方法实现
java 8之前,接口中不存在方法的实现 - 实现方式
子类使用extends关键字来继承抽象类,如果子类不是抽象类,子类需要提供抽象类中所声明方法的实现
子类使用implements来实现接口,需要提供接口中所有声明的实现 - 构造器
抽象类中可以有构造器, 接口中不能 - 访问修饰符
抽象方法可以有public,protected和default等修饰
接口默认是public,不能使用其他修饰符 - 多继承
一个子类只能存在一个父类 一个子类可以存在多个接口 - 访问新方法
想抽象类中添加新方法,可以提供默认的实现,因此可以不修改子类现有的代码
如果往接口中添加新方法,则子类中需要实现该方法
1.9 引用类型和原始类型
- int是java的原始类型,缺省值为0
- Integer是java为int提供的引用类型。Java为每个原始类型提供了封装类。缺省值为 null
自动装箱与拆箱
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型;
2.类
2.1 Object
- hashcode
hashCode()是Object类的一个方法,返回一个哈希值。对象在哈希表中的索引位置 - equal
如果两个对象根据equal()方法比较相等,那么调用这两个对象中任意一个对象的hashCode()方法必须产生相同的哈希值。
如果两个对象根据eqaul()方法比较不相等,那么产生的哈希值不一定相等(碰撞的情况下还是会相等的 - == 与equal
如果a 和b 都是对象,则 a == b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,
a.equals(b) 是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较
基本类型比较用 == ,比较的是他们的值。
默认下,对象用 == 比较时,比较的是内存地址,如果需要比较对象内容,需要重写equal方法。
2.2 内部类
2.2.1 内部类分类与定义
- 成员内部类
public class Outer {
private static int radius = 1;
private int count =2;
class Inner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
System.out.println("visit outer variable:" + count);
}
}
}
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.visit();
- 局部内部类
public class Outer {
private int out_a = 1;
private static int STATIC_b = 2;
public void testFunctionClass(){
int inner_c =3;
class Inner {
private void fun(){
System.out.println(out_a);
System.out.println(STATIC_b);
System.out.println(inner_c);
}
}
Inner inner = new Inner();
inner.fun();
}
public static void testStaticFunctionClass(){
int d =3;
class Inner {
private void fun(){
// System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量
System.out.println(STATIC_b);
System.out.println(d);
}
}
Inner inner = new Inner();
inner.fun();
}
}
- 匿名内部类
匿名内部类必须继承一个抽象类或者实现一个接口。
匿名内部类不能定义任何静态成员和静态方法。
当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。
匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
public class Outer {
private void test(final int i) {
new Service() {
public void method() {
for (int j = 0; j < i; j++) {
System.out.println("匿名内部类" );
}
}
}.method();
}
}
//匿名内部类必须继承或实现一个已有的接口
interface Service {
void method();
}
- 静态内部类
public class Outer {
private static int radius = 1;
static class StaticInner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
}
}
}
Outer.StaticInner inner = new Outer.StaticInner();
inner.visit();
2.2.2 静态Builder模式
// 静态工厂和构造函数都可以创建对象,局限性:参数过多可读性不好
public class BuilderTest {
private String name;
private Double num;
public BuilderTest(Builder builder){
}
public static class Builder{
private String name;
private Double num;
public BuilderTest build() {
return new BuilderTest(this);
}
}
public static void main(String[] args) {
BuilderTest builderTest = new BuilderTest.Builder()
.build();
}
}
2.2.3 final问题
局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final
因为生命周期不一致, 局部变量直接存储在栈中,当方法执行结束后,非final的局部变量就被销毁。
而局部内部类对局部变量的引用依然存在,如果局部内部类要调用局部变量时,就会出错。加了final,
可以确保局部内部类使用的变量与外层的局部变量区分开,解决了这个问题。
public class Outer {
void outMethod(){
final int a =10;
class Inner {
void innerMethod(){
System.out.println(a);
}
}
}
}
3.集合
1.List
1.1 ArrayList
内部数组实现,可以快速随机访问。数组大小不满足时,已有数组复制到新的空间中。适合查找遍历。
线性安全 Collections.synchronizedList
List synchronizedList = Collections.synchronizedList(new ArrayList<>());
ArrayList扩容机制
1、添加元素时,首先进行判断是否大于默认容量10
2、如果,小于默认容量,直接在原来基础上+1,元素添加完毕
3、如果,大于默认容量,则需要进行扩容,扩容核心是grow()方法
3.1 扩容之前,首先创建一个新的数组,且旧数组被复制到新的数组中
这样就得到了一个全新的副本,我们在操作时就不会影响原来数组了
3.2 然后通过位运算符将新的容量更新为旧容量的1.5陪
3.3 如果新的容量-旧的容量<=0,就拿新的容量-最大容量长度如果<=0的,那么最终容量就是扩容后的容量
1.2 Vector
数组实现,支持线程同步,因此访问慢,线程安全
1.3 LinkedList
链表结构,适合插入删除,有操作表头,表尾的方法,当做栈,队列,双向队列使用,线程不安全
2.Set
对象相等性是对象hashCode值(根据内存地址计算),俩个对象相同,必须覆盖hashCode和equals方法
2.1 HashSet
线程不安全
HashSet 首先判断两个元素的哈希值,如果哈希值一样,接着会比较
equals 方法 如果 equls 结果为true ,HashSet 就视为同一个元素。如果equals 为false 就不是同一个元素。
2.2 TreeSet
底层红黑树
- 按照二叉树原理对add新对象排序
- 自定义对象必须实现Comparable接口且覆盖compareTo函数
2.3 LinkedHashSet
使用LinkedHashMap保存元素,继承HashSet,方法上与其相同
3.Map
3.1 HashMap
- 根据键的hashCode 值存储数据
- 非线程安全,使用Collections 的synchronizedMap或ConcurrentHashMap
HashMap 默认的初始化⼤⼩为16。之后每次扩充,容量变为原来的 2 倍。
创建时如果给定了容量初始值, HashMap 会将其扩充为 2 的幂次⽅⼤⼩
3.1.1 Java 7实现
整体是数组,数组中每个元素是单向链表,扩容后为当前2倍
根据hash值定位到数组下标,但是需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)
3.1.2 Java 8实现
链表元素超过8个,将链表转化为红黑树。O(logN)
3.2 ConcurrentHashMap
线程安全,多线程环境下,使用 HashMap 进行put操作会引起死循环
3.2.2 Java7
Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个Segment 是线程安全的,也就实现了全局的线程安全。
整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多地方都会将其描述为分段锁。
每个段都是一个独立的哈希表,只包含总容量的一部分,每个段都有自己的锁,因此不同的线程可以同时访问不同的段。
这种方法在高并发场景下表现良好,但由于锁的开销和段的数量限制,它的性能随着并发度的增加而下降。
- 并行度concurrencyLevel:默认16,有16个segments段,最多支持同时16个线程并行写,该参不可扩容
3.2.3 Java8
整个哈希表被分成若干个段,每个段都被实现为一个数组,每个数组元素都是一个链表或红黑树。
每个元素都是一个桶,通过哈希函数将键值对映射到桶中。
每个桶内部都通过synchronized来实现同步,以保证线程安全性。而对于读操作,使用了无锁的CAS操作。
- 减少锁竞争:由于每个桶内部采用了synchronized来实现同步,不同的线程可以同时访问不同的桶,从而减少了锁竞争。
- 更高的并发度:由于不再受限于固定数量的段,ConcurrentHashMap 可以根据需要动态调整大小,并支持更高的并发度。
- 更好的扩展性:由于不再需要维护多个段的锁,因此在扩展时可以更容易地添加或删除桶,而不需要重构整个数据结构。
- 更好的性能:使用CAS操作替代了分段锁,避免了分段锁中的自旋等待开销,提高了并发性能。
3.3 HashTable
遗留类
3.4 TreeMap
红黑树实现,实现SortedMap接口,默认按key升序排序,必须实现Comparable接口或构造TreeMap时传入Comparator
3.5 LinkedHashMap
保存记录插入顺序
3.6 遍历key,value
- Set
Map<String, String> fieldsMap = new HashMap<>();
Set<String> keySet = fieldsMap.keySet();
for (String key : keySet) {
System.out.println(key + " " + fieldsMap.get(key));
}
- Iterator
Iterator iterator = keyset.iterator();
while(iterator.hasNext()){
Object key = iterator.next();
System.out.println(key+"-"+hashMap.get(key));
}
- entrySet()
Set set = hashMap.entrySet();
for(Object key:set){
Map.Entry entry = (Map.Entry) key;
System.out.println(entry.getKey()+"-"+entry.getValue());
}
- Map.Entry
Set set1 = hashMap.entrySet();
Iterator iterator1 = set1.iterator();
while(iterator1.hasNext()){
Object itset = iterator1.next();
Map.Entry entry = (Map.Entry) itset;
System.out.println(entry.getKey()+"-"+entry.getValue());
}
4.Collection工具类
4.1 排序
void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按⾃然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当distance为正数时,将list后
distance个元素整体移到前⾯。当distance为负数时,将 list的前distance个元
素整体移到后⾯
4.2 查找和替换
int binarySearch(List list, Object key)//对List进⾏⼆分查找,返回索引,
注意List必须是有序的
int max(Collection coll)//根据元素的⾃然顺序,返回最⼤的元素。 类⽐int
min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最⼤元素,排序
规则由Comparatator类控制。类⽐int min(Collection coll, Comparator c)
void fill(List list, Object obj)//⽤指定的元素代替指定list中的所有元素。
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第⼀次出现
的索引,找不到则返回-1,类⽐int lastIndexOfSubList(List source, list
target).
boolean replaceAll(List list, Object oldVal, Object newVal), ⽤新元素替
换旧元素
5.其他问题
5.1 快速失败
快速失败(fail-fast) 是 Java 集合的⼀种错误检测机制。
在使⽤迭代器对集合进⾏遍历的时候,我们在多线程下操作⾮安全失败(fail-safe)的集合类可能就会触发 fail-fast 机制,导致抛出
ConcurrentModificationException 异常。
另外,在单线程下,如果在遍历过程中对集合对象的内容进⾏了修改的话也会触发 fail-fast 机制。
不要使用foreach进行remove/add操作,要使用迭代器
5.2.安全失败
采⽤安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,⽽是先复制原有集合内容,在拷⻉的集合上进⾏遍历。
所以,在遍历过程中对原集合所作的修改并不能被迭代器检测到,故不会抛ConcurrentModificationException 异常。
5.3.Arrays.List
传递的数组必须是对象数组,⽽不是基本类型。
Arrays.asList() 是泛型⽅法,传⼊的对象必须是对象数组。
使⽤包装类型数组就可以解决这个问题
6.迭代器
6.1 遍历
Iterator<Map.Entry<Integer, Stringef iterator =
map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> entry = iterator.next();
System.out.println(entry.getKey() + entry.getValue());
}
7.Queue
7.1 PriorityQueue
无界优先级队列,需要选择合适初始容量,底层数组实现,堆排序算法来维护元素顺序。
优先级高的元素先出。
默认采用最小堆实现,第一个元素最小,也可以传Comparator自定义排序
4.泛型
1.表示类
public class Demo1<T> {
private T value;
//泛型方法
public <T> void fun(T x){
System.out.println();
}
}
2.表示接口
public interface Demo2<T> {
public T fun();
}
3.泛型通配符
public static void main(String[] args) {
List<String> strList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();
strList.add("lwx");
intList.add(1);
generic(strList);
generic(intList);
}
//泛型通配符
public static void generic(List<?> data){
System.out.println(data.get(0));
}
4.类型擦除
Java 中的泛型基本上都是在编译器这个层次来实现的。在生成的Java 字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除
5.反射
程序在运行中过程中,对于任何一个类都能知道它所有的属性和方法,对于任意一个对象,都能知道调用它的任意属性和方法。
1.获得setter,getter方法
MyObject myObject = new MyObject();
//反射调用getter方法
Method getMethod = myObject.getClass().getMethod("get" + "MyPropValue");
String res = getMethod.invoke(myObject).toString();
//反射调用setter方法
Method method = myObject.getClass().getMethod("set" + "MyPropValue", String.class);
method.invoke(myObject, "Value");
2.常见使用方法
获取反射:1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 1.
Student stu1 = new Student();
Class obj1 = stu1.getclass();
// 2.
Class student = Class.forName("Student");
// 3.
Class obj2 = Student.class;
// 获取公有属性
Field[] fields = student.getFields();
for(Field field : fields){
System.out.println(field);
}
//获取所有属性 不包含继承的
Field[] declaredFields = student.getDeclaredFields();
for(Field df : declaredFields){
System.out.println(df);
}
//获取所有公共方法
Method[] methods = student.getMethods();
for (Method method : methods){
System.out.println(method);
}
//获取所有方法 不包含继承的
Method[] declaredMethods = student.getDeclaredMethods();
for (Method dm: declaredMethods){
System.out.println(dm);
}
//获取所有公共构造方法
Constructor[] constructors = student.getConstructors();
//获取所有构造方法
Constructor[] declaredConstructors = student.getDeclaredConstructors();
//获取对象
Class c = Class.forName("Student");
Student student1 = (Student) c.newInstance();
// 获取对象利用构造函数
Constructor<Student> constructor = c.getConstructor(String.class, String.class);
Student student2 = (Student) constructor.newInstance("lwx", "name");
//获取方法
Method method = c.getMethod("fun");
Object object = method.invoke(student1);
}
}
3.ClassLoader类
类装载器用来把类装载到JVM中,使用双亲委派模型来搜索加载类
6.异常
1.分类
1.1 Error
系统内部错误,不会抛出,不应该被捕获,无法恢复,应该被声明
1.2 Exception
1.2.1 RuntimeException 运行时异常
应该被捕获,可以恢复,应该被声明
1.2.2 CheckedException 检查异常
应该被捕获,可以恢复,必须声明
2.异常处理
2.1 抛出异常
- throw
用在函数内,后面跟着异常对象。
执行到throw 功能结束 跳转调用者 - throws
用在函数上,后面跟多个异常类。
表示异常出现的可能性 - 系统自动抛出异常
2.2 捕获处理异常
try-catch-finally
- 无论执行情况如何 都会走finally
7.注解
1.元注解
- @Target 说明修饰对象范围
- @Retention : 定义该注解被保留的时间长短,SOURCE 源文件保留,CLASS class文件保留,RUNTIME 运行时保留
- @Documented :描述javadoc
- @Inherited : 阐述了某个被标注的类型是被继承的
2.注解处理
- TestProvider注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestProvider {
public int id() default -1;
public String name() default "";
public String address() default "";
}
- Apple
public class Apple {
@TestProvider(id = 1,name = "lwx",address = "aaa")
private String testProvider;
public String getTestProvider() {
return testProvider;
}
public void setTestProvider(String testProvider) {
this.testProvider = testProvider;
}
}
- Util
public class Util {
public static void getTest(Class<?> clazz){
String str = "lwx";
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields){
if (field.isAnnotationPresent(TestProvider.class)){
TestProvider testProvider = (TestProvider) field.getAnnotation(TestProvider.class);
str = testProvider.id() + testProvider.name();
System.out.println(str);
}
}
}
public static void main(String[] args) {
Util.getTest(Apple.class);
}
}
3.使用
- 数组属性
public @interface MyTest{
String[] value
}
@MyTest({"str","str1"})
8.复制
将一个对象的引用赋值给另一个对象
1. 直接赋值
A a1 = a2;
a1变化 a2也变化
2.浅复制
复制引用不复制引用的对象
创建新对象,将当前对象的非静态字段赋值到新对象,值类型复制,引用类型,复制引用而不复制引用的对象
public class Resume implements Cloneable {
public Object clone() {
try {
return (Resume) super.clone();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
3.深复制
复制对象本身,复制对象包含的引用指向的对象
public class Student implements Cloneable{
String name;
Resume resume;
public Student(String name, Resume resume) {
this.name = name;
this.resume = resume;
}
public Object clone(){
Student s = null;
try {
s = (Student) super.clone();
} catch (Exception e){
e.printStackTrace();
}
s.resume = (Resume) resume.clone();
return s;
}
}
9.枚举
- 枚举类
public enum Family {
FATHER,
SON
}
- 使用
public class EnumUse {
/**
* 1.自动添加toString
* 2.ordinal方法 表示声明顺序
* 3.values()方法
* @param args
*/
public static void main(String[] args) {
Family s = Family.FATHER;
for(Family family : Family.values()){
System.out.println(family + " " + family.ordinal());
}
}
}
- 与普通类类似
public enum OrdinalEnum {
WEST("Live in west"),
EAST("Live in east");
String descrption;
OrdinalEnum(String descrption) {
this.descrption = descrption;
}
public String getDescrption() {
return descrption;
}
public void setDescrption(String descrption) {
this.descrption = descrption;
}
public static void main(String[] args) {
for(OrdinalEnum ordinalEnum: OrdinalEnum.values()){
System.out.println(ordinalEnum.getDescrption());
}
}
}
10.IO
1.IO模型
1.1 BIO
同步并阻塞。服务器实现一个连接一个线程,客户端在有连接请求时服务器启动一个线程处理,没处理完不能做其他操作。
适用于连接数目小且固定的架构。
- 面向流,只能单向读写,没有数据时会挂起线程阻塞等待,需要对每个连接新建线程处理
- BIOServer
public class BIOServer {
/**
* Server启动 client01启动 client02启动(被阻塞,01输入之后开始连接)
* @param args
*/
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream = null;
OutputStream outputStream = null;
try {
serverSocket = new ServerSocket(8000);
System.out.println("8000");
while (true){
//等待客户端连接
socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress());
inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
//读取客户端数据
while ((len = inputStream.read(bytes)) >0 ){
System.out.println(new String(bytes, 0, len));
}
//向客户端写数据
outputStream = socket.getOutputStream();
outputStream.write("Hello BIO".getBytes(StandardCharsets.UTF_8));
}
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 多线程启动
*/
public void BIOThreadServer(){
try {
ServerSocket serverSocket = new ServerSocket(8000);
System.out.println("8000");
while (true){
//等待客户端连接
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress());
//多线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
//读取客户端数据
while ((len = inputStream.read(bytes)) >0 ){
System.out.println(new String(bytes, 0, len));
}
//向客户端写数据
outputStream = socket.getOutputStream();
outputStream.write("Hello BIO".getBytes(StandardCharsets.UTF_8));
} catch (Exception e){
e.printStackTrace();
}
}
});
thread.start();
}
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 线程池
*/
public void BIOThreadPool(){
//线程池
ExecutorService executorService = Executors.newFixedThreadPool(30);
try {
ServerSocket serverSocket = new ServerSocket(8000);
System.out.println("8000");
while (true){
//等待客户端连接
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress());
//多线程
executorService.execute( new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
//读取客户端数据
while ((len = inputStream.read(bytes)) >0 ){
System.out.println(new String(bytes, 0, len));
}
//向客户端写数据
outputStream = socket.getOutputStream();
outputStream.write("Hello BIO".getBytes(StandardCharsets.UTF_8));
} catch (Exception e){
e.printStackTrace();
}
}
}));
}
} catch (Exception e){
e.printStackTrace();
}
}
}
- BIOClient
public class BIOClient01 {
public static void main(String[] args) throws Exception{
Socket socket = new Socket("127.0.0.1",8000);
//字节流
OutputStream outputStream = socket.getOutputStream();
//IO方式发送到服务器
System.out.println("连接成功");
String str = new Scanner(System.in).nextLine();
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
}
}
1.2 NIO
同步非阻塞。服务器实现一个连接一个线程,客户端发送的请求注册到多路复用器上,轮询到有IO请求才启动线程处理。
适合连接数目多且连接短的架构。
- 面向缓冲,可以双向读写,进行多路复用,一个线程监听多个连接
- 通道:Channel通过它读写数据,数据先写入缓冲区,再将其写入channel,再从channel读到缓冲区
- 选择器:Selector,channel注册到selector中
- 缓冲区:Buffer
- NIOServer
public class NIOServer {
public static void main(String[] args) throws Exception{
//service端的Channel 监听端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//非阻塞
serverSocketChannel.configureBlocking(false);
//赋值端口
serverSocketChannel.bind(new InetSocketAddress(8000));
// 绑定的地址
System.out.println(serverSocketChannel.getLocalAddress());
//声明Selector选择器
Selector selector = Selector.open();
//注册
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//轮询 客户端连接就取出来channel
while (true) {
int select = selector.select();
if (select == 0){
continue;
}
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
//连接客户端和服务端
SelectionKey key = iterator.next();
//判断状态
if (key.isAcceptable()){
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
//客户端对应的Channel
SocketChannel socketChannel = channel.accept();
System.out.println(socketChannel.getRemoteAddress());
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
//想读要先写到buffer
channel.read(byteBuffer);
String request = new String(byteBuffer.array()).trim();
byteBuffer.clear();
System.out.println(request);
String response = request;
channel.write(ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8)));
}
iterator.remove();
}
}
}
}
- NIOClient
public class NIOClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("127.0.0.1",8000);
OutputStream outputStream = socket.getOutputStream();
while (true){
byte[] bytes = new Scanner(System.in).nextLine().getBytes(StandardCharsets.UTF_8);
outputStream.write(bytes);
}
}
}
1.3 AIO
异步非阻塞。服务器一个有效请求一个线程,客户端请求由操作系统完成后通知服务器启动线程。
适用于连接数目多且长的架构
1.4 Netty
-
提供异步事件驱动的网络应用程序框架和工具,用以快速开发高性能,高可靠的网络服务器和客户端程序。
-
基于NIO的客户服务器框架,快速开发网络应用
-
NettyServerHandler
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel channel = ctx.channel();
String request = (String) msg;
String response = request;
ctx.write(response);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
System.out.println(channel.remoteAddress());
}
}
- NettyServer
public class NettyServer {
public static void main(String[] args) throws Exception {
EventLoopGroup boosGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boosGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new StringDecoder());
pipeline.addLast("encoder", new ObjectEncoder());
pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
pipeline.addLast(new NettyServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
} catch (Exception e){
}
}
}
1.5 信号驱动IO
1.6 IO多路转接
2.Java IO流
2.1 字符流:字符为单位,16位数据 char
- 节点流Reader
/**
* 节点流
*/
public void NodeStream() throws Exception{
int num = 0;
//必须存在 read()自动吧char转化为int
FileReader fileReader = new FileReader("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
//第一种
char[] buf = new char[1024];
while ((num = fileReader.read(buf)) != -1) {
System.out.print((new String(buf,0,num)));
}
//第二种
while ((num = fileReader.read()) != -1) {
System.out.print((char)num);
}
}
- 处理流Reader
/**
* 处理流
*/
public void handlerStream() throws Exception{
String[] bufString = new String[1024];
int num = 0;
FileReader fileReader = new FileReader("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = null;
//readLine 单行读取
while ((line = bufferedReader.readLine()) != null){
bufString[num] = line;
num++;
}
bufferedReader.close();
for (int i = 0; i < num; i++){
System.out.println(bufString[i]);
}
}
- 节点流Writer
/**
* 节点流写入
* @throws Exception
*/
public void nodeStreamWriter() throws Exception{
File file = new File("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
//true代表追加 不写就覆盖文件
FileWriter fileWriter = new FileWriter(file, true);
// \n换行
String str = "\nLWX一二三";
fileWriter.write(str);
fileWriter.close();
}
- 处理流Writer
/**
* 处理流Writer
* @throws Exception
*/
public void handlerStreamWriter() throws Exception{
File file = new File("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
//true代表追加 不写就覆盖文件
FileWriter fileWriter = new FileWriter(file, true);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
String str = "\n一二三LWX";
bufferedWriter.write(str);
bufferedWriter.close();
}
2.2 字节流:字节为单位,8位 byte
- 节点流
/**
* 节点流
*/
public void nodeReader() throws Exception {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test1.txt");
byte[] bytes = new byte[fileInputStream.available()];
//读取到数组
fileInputStream.read(bytes);
fileOutputStream.write(bytes);
fileInputStream.close();
fileOutputStream.close();
}
- 处理流
/**
* 处理流
* @throws Exception
*/
public void handlerStream() throws Exception{
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test1.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//循环输出
int i;
while ((i = bufferedInputStream.read()) != -1){
bufferedOutputStream.write(i);
}
bufferedInputStream.close();
bufferedOutputStream.close();
}
2.3 Java NIO
NIO三个部分:Channel通道 Buffer缓冲区 Selector选择区
传统IO基于字节流和字符流,NIO基于通道和缓冲区操作,数据从通道读到缓冲区,或者从缓冲区读到通道。
选择区用于监听多个通道事件
IO面向流,NIO面向缓冲区
IO面向流表示没有被缓存,不能前后移动数据。NIO缓存区可以前后移动数据。NIO是非阻塞的
2.5.1 Channel
双向的。
主要实现有FileChannel DatagramChannel SocketChannel ServerSocketChannel 对应文件IO UDP tcp
2.5.2 Buffer
读写的数据都必须经过buffer
2.5.3 Selector
检测多个注册的通道上是否有事件发生,可以获取事件并进行处理
一个单线程可以管理多个通道,当有事件发生时,才进行处理,不需要都去创建线程,避免线程上下文开销
3.序列化
- 序列化:二进制形式保存在硬盘上,不保存静态变量
- 反序列化: 二进制文件转化为对象读取
3.1 Serializable接口
- SerialVersionUID: 接口默认生成,默认生成在反序列化时可能会导致InvaildClassException异常(序列化与反序列化比对通过UID进行,反序列化之前修改类,就会报错)
- static final long SerialVersionUID =