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 =
posted @ 2025-09-10 17:24  lwx_R  阅读(4)  评论(0)    收藏  举报