JAVA

作者: saoraozhe3hao | 来源:发表于2018-08-03 15:26 被阅读0次

    概念

    JVM:Java Virtual Mechinal,Java虚拟机,能让同一字节码,在不同的操作系统上,运行出相同结果
    JRE:Java Runtime Environment,Java 运行时环境,包含JVM、核心类库和支持文件
    JDK:Java Development Kit,包含JRE、Java工具和基础类库;曾称为Java SDK(Software Development Kit)
    OpenJDK:Sun公司2006年把JDK开源而形成的项目
    JDK下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html
    JDK在线文档:https://docs.oracle.com/javase/8/docs/api/
    JDK源码:jdk/src.zip
    Java SE:Java Standard Edition,Java标准版
    Java EE:Java Enterprise Edition,Java企业版,是Java SE的超集,曾称为J2EE。Java EE和Java SE用的JDK都是Java SE JDK,但多了一些规范(文档)和接口,例如JDBC、JNDI、RMI、JSP、Servlet
    Java工具:javac命令 把.java编译成字节码文件.class,java命令(根包的同级目录下,即src下执行) 让JVM执行.class文件,javadoc命令 生成文档,jar命令 打jar包
    编码规范:《阿里巴巴Java开发手册》

    安装JDK

    Windows上安装JDK
    1、oracle网站上下载JDK,双击安装到java目录,安装后java目录下有jdk 和 jre
    2、环境变量->系统变量->Path->编辑->加入java/jdk/bin,用于java命令的查找;path里已有的JRE路径要删除,可以把新增的jdk/bin上移到第一行
    3、新增java_home变量,设为java目录,一些JAVA程序要用到,比如maven、tomcat;此步按需操作
    4、引用JDK官方包,新增classpath变量,值为.;%JAVA_HOME%\jdk\lib\tools.jar; tools.jar为编译和运行用到的一些类库;运行环境此步可以省略
    Windows上使用第三方JAR包:classpath 加入第三方JAR包路径

    Linux上安装JDK
    1、Oracle网站上下载 **.tar.gz;上传到服务器 /usr/java
    2、解压解包:tar -zxvf **.tar.gz
    3、在 /etc/profile 追加

    export JAVA_HOME=/usr/java/jdk**
    export JRE_HOME=${JAVA_HOME}/jre
    export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib:$CLASSPATH
    export JAVA_PATH=${JAVA_HOME}/bin:${JRE_HOME}/bin
    export PATH=$PATH:${JAVA_PATH}
    
    立即生效:source /etc/profile
    4、测试:javac;java -version;echo $PATH
    

    数据类型

    整型:byte(1字节) short(2字节) int(4字节,超过20亿) long(8字节)
    浮点类型:float(4字节,6位有效) double(8字节,15位有效)
    char类型:char(UTF-16 两字节字符)
    boolean类型:boolean
    枚举类型:public enum Size{SMALL,MEDIUM,LARGE}; Size.SMALL是 Size 的实例 Size size = Size.SMALL;Size[] sizes = Size.values();
    数组:int[] arr = new int[100]; for(int e : arr){}; int [][] arr = {{1,2},{3,4}};

    基础类库

    java.lang下的类不用导入
    java.lang.Math
    java.lang.String
    java.lang.StringBuilder -> append(),toString()
    java.math.BigInteger ->大整数计算
    java.math.BigDecimal ->大浮点数计算
    java.util.Date ->处理时间
    java.time.LocalDate ->处理日历,LocalDate.now(),LocateDate.of(1999,2,2)
    字符串编辑:String(不可变字符串)、StringBuffer(字符串数组,性能高,线程安全)、StringBuilder(性能最高,线程不安全)
    CharSequence接口:String, StringBuffer, StringBuilder的父接口
    运行时:Runtime.getRuntime(); 内存信息freeMemory();垃圾回收gc();
    系统:System; 数组拷贝arraycopy(); 时间currentTimeMillis();垃圾回收gc();
    UUID:Universally Unique Identifier,根据MAC地址和时间戳生成的ID。UUID.randomUUID();
    Base64:Base64.Encoder,Base64.Decoder
    Serializable接口:是个标记接口,实现该接口的类可以被序列号;transient属性不被序列化
    属性操作:Properties类

    OOP:面向对象程序设计

    类与类之间的关系:依赖(use-a)、聚合(has-a)、继承(is-a)

    package包;       // 包名需与文件路径一致
    import包或类;
    public class Employee extends Person{  //没写继承类时,默认继承Object
        成员变量
        初始化块
        static main函数
        构造函数
        成员函数
        finalize函数
        内部类    //用outer.访问外部类的成员
    }
    

    访问权限修饰符:default(包内可访问)、public、protected(包内,子类可访问)、private
    访问权限修饰对象:类,成员变量,成员函数
    访问权限作用场景:访问其他类,访问其他类的成员,访问超类的成员(子类继承超类的所有成员,但未必有权限直接访问)
    final类:不能被继承
    final成员变量:声明时或者构造时必须初始化,并且初始化后不能被改变
    final成员函数:不能被重写
    成员static修饰符:表示能通过类访问,并且被所有实例共享
    初始化块:{}代码块,用于在构造实例前初始化成员变量
    函数重载(overload,重复定义):构造函数和成员函数都可以重载,即函数名一样,参数类型或个数不一样
    构造函数:构造函数之间可以互相调用
    无参构造器:写一个无参构造器时,成员变量会被自动初始化为0\false\null
    默认构造器:没写构造器时,默认为无参无体构造器
    finalize方法:在对象被回收前调用
    Object成员函数:equals(),toString(),getClass(),hashCode(),clone(),析构finalize()
    实例.getClass() ==类.class 包含类的各种信息
    成员重写(override,重新定义):子类成员与父类成员同名同形参
    超类调用:调用被本类重写的超类成员,super.成员名;调用超类构造函数super()
    多态(polymorphism,多型态):超类类型的变量 可以引用 自己的任何子孙类实例,即一个变量可以有不同的形态,但可以调用的成员只能是超类有的成员
    动态绑定:子类实例调用成员时,根据成员名和参数动态确定调用的是子类成员还是超类成员
    工厂方法意义:构造只能产生本类的实例,工厂方法可以根据输入返回不同子类的实例
    创建接口的子类实例,简写法

    new Runnable(){
        public void run() {
        }
    }
    

    类型转换
    默认数值转换:优先顺序double\float\long\int
    强制数值转换:(int) 3.1
    强制类型转换:Employee em = (Employee) person;将超类类型的变量 转换 为子类类型的变量,前提是person引用的对象本来就是Employee或Employee子类的实例
    抽象类

    public abstract class Person{    //抽象类,不能实例化,可以被抽象类和普通类继承
        成员变量
        构造函数
        成员函数
        public abstract String getName(); // 抽象函数,只能出现在抽象类中。本类的普通子类必须重写所有抽象类
    }
    

    包装器
    Byte、Short、Integer、Long、Float、Double、Character、Void、Boolean
    包装(装箱):Integer i = Integer.valueOf(3);自动装箱 Integer i = 3;自动装箱由编译器完成

    接口

    public interface athlete{ // 能被其他接口extends,能被类 implements
        final static I = '';  // 成员变量只能是静态常量
        int swim();          // 抽象方法
        default int run(){return 2} // 默认方法
    }
    public class Employee extends Person implements interface1,interface2{  //实现多接口
        普通类必须定义接口的所有抽象方法
        超类和接口默认方法重复时,以超类方法为准
    }
    

    异常类
    Throwable ->异常Exception 、 系统内部错误Error
    Exception ->运行时异常RuntimeException、IOException、ClassNotFoundException
    RuntimeException:类型转换错误,空指针NullPointerException,数组越界ArrayIndexOutOfBoundsException
    IOException:文件访问错误等,编译器会检查这种异常是否有处理器,因此也叫受查异常。IOException的子类都是受查异常

    异常处理

    //本方法可能抛出Error、RuntimeException、IOException,但只有受查异常IOException需要声明
    public Image loadImage(String s) throws IOException{ 
        //调用了声明throws IOException的方法且不捕获 或者 本方法中有throw new IOException(),则需要声明 throws IOException
        try{
        }catch(RuntimeException e){  // 捕获RuntimeException异常,{}里为异常处理器
            e.printStackTrace();
        }finally{
            // try里不管有没有抛出异常,有没有return,抛出的异常有没有被catch到,都会进到这里
        }
    }
    

    子类重写父类中声明异常的方法时,声明的异常范围不能超过父类

    泛型方法,可以在普通类中,也可以在泛型类中

    public <T,U> T getName(T firstName, U lastName){  // <T, U>是泛型,T是函数返回类型
        return firstName;
    }
    

    泛型方法在被调用时需定型object.<String, Object>getName("first", "last");

    泛型类

    public class Pair<T,U>{
        private T temp;
        private U user;
    }
    

    定义泛型类(可以定型限定)
    Pair<T>,不限定,类定义中T只拥有Object有的成员方法
    Pair<T extends Employee>,被定型时限定在Employee子类,因此类定义中T拥有Employee有的成员方法
    Pair<T extends 类1&接口1> pair,被定型时限定在类1&接口1的子类,因此类定义中T拥有类1和接口1有的成员方法

    构造对象(需定型)
    构造对象时定型:new Pair<String, Object>,不能用基本类型定型

    被继承(可以定型,可以不定型,也可以部分定型)
    被继承时不定型:SPair<K,V> extends Pair<K, V>,父类不定型,子类得保留父类的泛型K,V,才能保证构造子类对象时父类的泛型都被定型
    被继承时部分定型:SPair<V> extends Pair<String, V>,父类部分定型,子类得保留父类未定型的泛型V,并且子类中不能再用K来声明变量
    被继承时定型:SPair<T> extends Pair<String, Object>,父类定型,子类可以新开泛型T

    声明变量(可以定型限定)
    Pair<?> pair 或 Pair pair,不限定
    Pair<Employee> pair,只能引用new Pair<Employee>
    Pair<? extends Employee> pair,只能引用 new Pair<Employee子类>
    Pair<? super Employee> pair,只能引用new Pair<Employee超类>

    泛型翻译(泛型擦除)
    定义擦除
    泛型类Pair<T extends 接口1&类1, U>,擦除成原始型Pair;类定义中T会被替换成第一个限定类,即接口1;U 会被替换成Object
    构造擦除
    new Pair<String>得到的对象,调用其方法的地方,T型入参和T型返回值将用(String)强制类型转换
    声明擦除
    Pair<?> 擦除成 Pair
    继承擦除
    SPair extends Pair<String>,SPair 对象,调用父类方法的地方,T型入参和T型返回值将用(String)强制类型转换

    不支持泛型数组,函数中不能实例化T,不能用T声明静态成员

    集合
    java.util.Collection接口,add(),remvoe(),iterator(),size(),isEmpty(),contains(),toArray(),clear(),equals() -> List列表,Set集合,Queue队列
    java.util.Iterator接口,next(),hasNext(),remvoe() -> ListIterator
    java.util.Map接口,映射,size(),get(),put(),remove()
    java.util.List接口,get(),indexOf(),subList()
    java.util.Set接口
    java.util.Queue接口,poll(),peek(),offer() -> Deque接口
    java.util.Deque接口,双端队列,addFirst(),addLast(),getFirst(),getLast(),pop(),push()
    java.util.ListIterator接口,add(),hasPrevious(),previous(),set()
    java.util.ArrayList类:用数组实现的List,删除一个元素时需要平移后续元素,listIterator(),sort()
    java.util.ArrayDeque类:用数组实现的Deque
    java.util.LinkedList类:用链表实现,既是List,也是双端Queue,listIterator()
    java.util.HashSet类:用链表数组实现,元素hash值即Object.hashCode(),插入和查找速度取决于hash桶(数组)内已有元素的多少
    java.util.TreeSet类:用红黑树实现,有序集合,插入比HashSet慢,查找速度取决于树的高度
    java.util.HashMap类:用链表数组实现,keySet(),replace(),values()
    java.util.TreeMap类:用树实现,firstKey(),floorKey(),keySet(),lastKey(),lowerKey(),subMap(),replace()
    java.util.WeakHashMap类:弱引用,Map中的对象没有其他地方引用,就会被删除
    java.util.LinkedHashSet类:在HashSet的基础上,每个元素都记录下一元素的位置,使得遍历是有序的
    java.util.LinkedHashMap类:在HashMap的基础上,每个元素都记录下一元素的位置,使得遍历是有序的
    java.util.EnumSet类:用位序列实现,用于存放 枚举类型实例(个数有限)
    java.util.EnumMap类:用于 Map的键类型为枚举类型的场景

    集合只读视图,集合的只读拷贝
    列表只读视图:List list = Collections.unmodifiableList(originList)
    集合只读视图:Set set = Collections.unmodifiableSet(originSet)
    映射只读视图:Map map = Collections.unmodifiableMap(originMap)、originMap.values()、originMap.keySet()、originMap.entrySet()
    数组转视图:List list = Arrays.asList(array);
    视图转集合:Set set = new HashSet(Arrays.asList(array));
    集合转数组:String[] values = list.toArray(new String[0]); //传入空数组,指示toArray返回String[] 类型的实例,否则返回的是Object[]类型实例
    这个toArray是个泛型方法,由于传入的是String[],T定型为String,返回值为String[]

    反射reflective,能够分析类能力的程序
    1、类分析
    获取本类的Class实例:Class class = obj.getClass(); // 一个类的不同实例所包含的Class实例是相同的
    类包含一个Class实例:Class class = Main.clss // 基本类型和数组也包含Class实例,int.class,Object[].class
    根据类名获取Class实例:Class class = Class.forName("Main") // 不适用与基本类型
    Class实例:类所在的包 getPackage(),类名 getName()
    构造新对象:class.newInstance(); //调用无参构造器构造
    2、数组分析
    判断是不是数组:class.isArray();
    获取数组元素的类型:class.getComponentType()
    构造新数组:java.lang.reflect.Array.newInstance(type,length)
    3、成员字段(域)分析
    得到Field实例:Field field = class.getField(name);
    获取字段的值:field.get(obj),等同于 obj.字段名
    设置字段的值:field.set(obj, value);
    4、成员函数分析
    得到Method实例:Method method = class.getMethod(String name)
    执行函数:method.invoke(obj, ...args)
    5、构造函数分析
    得到Constructer实例:Constructor constructor = class.getConstructor(parameterTypes)
    构造信对象:constructor.newInstance(...initargs)
    6、修饰符分析
    得到整数表示:int modifiers = class.getModifiers()
    得到字符串表示:java.lang.reflect.Modifiers.toString(modifiers)

    典型用法
    工厂方法中,根据传进的类名,返回不同的子类实例:Class.forName(className).newInstance();
    根据程序动态生成的方法名来调用指定方法:class.getMethod(methodName).invoke(obj);
    Comparable接口,实现该接口的类,其实例组成的数组可以被Arrays.sort(数组)排序,列表排序有 Collections.sort(list)

    public class Employee implements Comparable
    {
        public int comparaTo(Employee other){
            return Integer.compare(1,2);  //负数表示本对象小,0表示相等,正数表示本对象大
        }
    }
    

    Comparator接口,实现该接口的类,调用Arrays.sort(数组, Comparator实例)可以排序,调用Collections.sort(list, Comparator实例)可以排序

    class EmpComparator implements Comparator
    {
        public int compara(Employee e1, Employee e2){
            return 0;
        }
    }
    

    Cloneable接口,是一个标记接口,即空接口,只是为了标记 obj instanceof Cloneable

    class Employee implements Cloneable
    {
        public Employee clone(){            //把Object的protect重写成public
            return (Employee) super.clone(); //浅拷贝
        }
    }
    class Employee implements Cloneable
    {
        private Date date;
        public Employee clone(){
            Employee em = (Employee) super.clone();
            em.date = (Date) date.clone();  // 第二层拷贝,如果每一层都实现了二层拷贝,那么久实现了深拷贝
            return em;
        }
    }
    

    lambda表达式:(String first,String second)->{ return first.length - second.length; }
    lambda表达式会转换为函数式接口的子类实例,函数式接口即只有一个方法的接口
    典型应用:Arrays.sort(数组,lambda表达式)
    现成方法作为lambda表达式:Class::staticMethod,object::instanceMethod
    Comparator的工厂方法输出Comparator实例:Comparator.comparing(Person::getName);

    断言
    assert <布尔表达式> : <错误信息> ,表达式为false则抛出异常AssertionError(继承自Error)
    断言默认不生效,除非以断言模式来运行程序:IDEA -> Run -> Edit Configurations -> VM options -> -ea
    使用场景:写在逻辑上<布尔表达式>必定为ture的地方,如果抛出异常,说明这之前代码逻辑错误,从而缩小定位范围

    注解Annotation
    APT:(Annotation Processing Tool),提取和处理 Annotation 的工具
    注解的识别者:编译器、java工具、APT,例如让编译器对开发者提示一些信息
    JAVA内置注解:废弃提醒@Deprecated、必须准确重写@Override、函数式接口提示@FunctionalInterface、忽略提醒@SuppressWarnings("deprecation")
    元注解(注解的注解):@Retention、@Documented、@Target、@Inherited、@Repeatable
    @Retention(RetentionPolicy.RUNTIME) //注解保留期,- SOURCE 保留到源码阶段,可以被javadoc获取 - CLASS 保留到编译阶段,可以被编译器获取 - RUNTIME 保留到运行阶段,可以被APT获取
    @Documented // javadoc中@TestAnnotation注解的方法前会有"@TestAnnotation"字样
    @Target(ElementType.TYPE) //注解目标,CONSTRUCTOR 目标是构造方法,FIELD 目标是属性,LOCAL_VARIABLE 目标是局部变量,METHOD 目标是方法,PARAMETER 目标是方法内的参数,TYPE 目标是类型,比如类、接口、枚举
    @Inherited //注解继承,如果@TestAnnotation注解的父类,其子类没有注解的话,则继承父类的注解

    public @interface TestAnnotation{ // 自定义注解
        public int id();              //注解的属性
        public String msg() default "Hi"; //带默认值的属性
    }
    // 使用注解
    @TestAnnotation(id=3)
    public class Test{    }
    

    APT
    是否应用了某注解:Test.class.isAnnotationPresent(TestAnnotation.class)
    获取注解对象:TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
    获取所有注解:Test.class.getAnnotations()
    获取注解属性:testAnnotation.id()
    除了Class,Method和Filed也有同样的方法

    JAR:Java Archive,Java归档文件
    打JAR包:jar -cvf file.jar *.class。生成的jar文件里包含一个META-INF目录,里面有一个清单文件MANIFEST.MF
    解JAR包:jar -xvf file.jar
    MANIFEST.MF

    Manifest-Version: 1.0
    Created-By: hogen
    Main-Class: com.hogen.Main  // 主类,要生成可执行jar时,必写
    name: com.hogen
    Sealed: true  // 是否对com.hogen 封包,封包即不允许在使用本jar时有别的类同包
    

    打包时指定清单文件:jar -cvfm file.jar file.MF *.class
    运行jar文件:java -jar file.jar

    1.8新特性
    可变参数
    方法定义:int add(int ...data){print(data[0])}
    方法调用:add(new int[]{1,2})或 add(1,2)
    foreach循环
    for(int x :数组或集合){}
    接口定义加强
    default声明的方法,需要有方法体,可以通过对象调用
    static声明的方法,需要有方法体,可以通过接口名调用
    函数式接口@FunctionalInterface,lambda表达式、::方法引用
    java.util.function下都是一些函数式接口,可以用来接收lambda表达式

    多线程

    class MyThread extends Thread{
        public void run(){  // 线程入口方法
        }
    }
    
    new MyThread().start(); // 启动线程,会让JVM给线程分配资源后,调用run()
    
    class MyRunable implements Runable{  // Runable是个函数式接口,Thread是Runable的子类
        public void run(){
        }
    }
    
    Runable runable = new MyRunable(); 
    new Thread(runable).start(); // 启动线程,可以用同一个runable启动多个线程,多个线程共享runable的数据
    
    class MyCallable implements Callable{
        public String call(){  // 线程入口方法
        }
    }
    
    FutureTask task = new FutrueTask(new MyCallable());
    new Thread(task).start(); // 启动线程
    task.get(); // 获取线程入口方法返回值
    

    线程状态:start() ->就绪 -> 运行 -> 阻塞 -> 就绪 -> 运行 -> 终止
    内存模型:线程操作的是其工作内存,线程间共享的变量同时存在于主内存和工作内存,并在两者之间进行同步

    class MyRunable implements Runable{
        private volatile int i = 0; // volatile声明的变量,读取前会先从主内存同步到工作内存,修改后会马上从工作内存同步到主内存,以保证所有线程对该共享变量变化的“可见性”
        public void run(){
            this.i ++; // 次操作非原子操作(不可穿插),可分为读取,加一,赋值
            synchronized (this){  // 同步代码块,线程执行到这里时将获得对象锁 或 阻塞,保证代码块的原子性(不可穿插)
                Thread.currentThread(); // 获取当前线程
                Thread.sleep(1000);  // 当前线程休眠
                this.wait(); // 让当前线程让出对象锁,并进入 本对象的 等待区(wait set),即进入等待状态
                this.notify();  // 随机通知本对象 等待区 里的 一个线程 重回活跃状态,即可以重新进入同步代码块
                this.notifyAll();  // 通知本对象 等待区 里的 所有线程 重回活跃状态
            }
            syn();
        }
        public synchronized void syn(){ // 同步方法,线程执行到这里时将获得对象锁 或 阻塞
        }
        public static synchronized void csyn(){ // 静态同步方法,线程执行到这里时将获得类锁 或 阻塞
        }
    }
    

    Thread thread = new Thread(new MyRunable(), "线程名"); // 给名线程
    thread.getName(); // 获取线程名,程序主线程的名称为main
    thread.setPriority(Thread.MAX_PRIORITY); // 设置线程优先级,主线程优先级为NORM_PRIORITY
    thread.setDaemon(true); // 设置为守护线程,普通线程都结束时,守护线程自动结束
    thread.start();

    线程池
    线程池工具:Executors
    无上限线程池:newCachedThreadPool()
    固定线程池:newFixedThreadPool(int)
    单线程池:newSingleThreadExecutor()
    调度线程池:newScheduledThreadPool(int)

    // ExecutorService线程池
    ExecutorService es = Executors.newCachedThreadPool();
    es.submit(new MyRunable());  // 提交任务,可利用空闲线程
    es.shutdown();  // 关闭线程池
    // ScheduledExecutorService线程池
    ScheduledExecutorService ses = Executors.newScheduledThreadPool();
    ses.scheduleAtFixedRate(new MyRunable(), 1, 2, TimeUnit.SECONDS);  // 1s 后 每2s 执行一次任务
    ses.shutdown();  // 关闭线程池
    

    定时器

    class MyTask extends TimerTask{
        public void run(){
        }
    }
    Timer timer = new Timer();
    timer.schedule(new MyTask(), firstTime, 4); //  从firstTime开始,每4s后并且上次执行完成,则执行下一次,不会出现多个任务同时执行
    timer.scheduleAtFixedRate(new MyTask(), firstTime, 4); // 从firstTime开始,每4s发起一次任务,出现拖延时会有多个任务同时执行
    

    另有第三方开源任务调度框架quartz,拥有timer不具备的容错能力和复杂任务调度能力

    IO
    File类
    File file = new File("文件路径或目录路径"); // 可以是不存在的路径
    静态成员:分割符File.separator
    文件方法:创建本文件file.createNewFile(),文件大小file.length(),是否是文件file.isFile()
    目录方法:创建本目录file.mkdir(),是否是目录file.isDirectory(),列出所有文件file.listFiles()
    共有方法:获取父目录getParent(),删除file.delete(),创建所有还不存在的上级目录file.mkdirs()
    字节输出流 抽象类OutputStream
    OutputStream os = new FileOutputStream(file, 是否是追加);
    输出字节数组os.write("".getBytes()),冲刷缓存os.flush(),关闭流 os.close()
    字节输入流 抽象类InputStream
    InputStream is = new FileInputStream(file);
    读取到字节数组is.read(byte[]),关闭流 os.close(),字节数组转字符串new String(byte[])
    字符输出流 抽象类Writer,处理多字节字符
    Writer fw = new FileWriter(file); // FileWriter继承OutputStreamWriter,OutputStreamWriter继承Writer
    输出字符串fw.write(""),冲刷缓存fw.flush(),关闭流 fw.close()
    字符输入流 抽象类Reader,处理多字节字符
    Reader fr = new FileReader(file); // FileReader 继承InputStreamReader,InputStreamReader继承Reader
    读取到字节数组fr.read(char[]),关闭流 fr.close(),字符数组转字符串new String(char[])
    字节流转字符流:Writer osw = new OutputStreamWriter(os); Reader isr = new InputStreamReader(is);
    内存操作流:ByteArrayOutputStream、ByteArrayInputStream、CharArrayWriter、CharArrayReader
    缓冲输出流:BufferedOutputStream、BufferedWriter,写入硬盘之前先写入缓冲区,缓冲区满了再写入硬盘
    缓冲输入流:BufferedInputStream、BufferedReader,一次性从硬盘读取数据把缓冲区填满,缓冲区被读完时,再次填满

    序列化
    ObjectOutputStream oos = new ObjectOutputStream(os);
    oos.writeObject(obj);
    反序列化
    ObjectInputStream ois = new ObjectInputStream(is);
    oos.readObject();

    网络编程
    服务器端

    class MyRunnable implements Runnable{  // 线程
        private Socket socket;
        public MyRunnable(Socket socket){
            this.socket = socket;        // 每个线程维护一个socket
        }
        public void run(){
            Scanner scanner = new Scanner(socket.getInputStream());  // 输入流扫描
            scanner.useDelimiter("\n");    // 换行符
            PrintWriter pw = new PrintWriter(socket.getOutputStream());  // 输出流打印
            if (scanner.hasNext()) {
                scanner.next().trim();
            }
            pw.println("help");
            scanner.close();  //关闭输入流
            pw.close();      //关闭输出流
        }
    }
    ServerSocket ss = new ServerSocket(9999); // 服务器,监听9999端口
    boolean flag = true;
    while(flag){                        // 持续监听
        Socket socket =  ss.accept();  // 服务器端socket
        new Thread(new MyRunnable(socket)).start();  // 开启新线程处理socket请求
    }
    Thread.sleep(5000);
    flag=false;    // 非强制停止线程
    ss.close();      //关闭服务器
    

    客户端

    Socket socket = new Socket("localhost",9999);  // 连接服务器
    处理socket.getInputStream() 和 socket.getOutputStream()
    socket.close();
    

    相关文章

      网友评论

          本文标题:JAVA

          本文链接:https://www.haomeiwen.com/subject/qtzyvftx.html