JAVASE学习笔记

1.导包

ctrl+shift+字母o ,回车

2.保留指定位数的小数

(1)保留几位小数  “%.nf” ,n保留的小数位数 ,保留2位小数 "%.2f"

(2)//借助 java.text.DecimalFormat类 ,格式化数字  保留几位小数,就留几个“0”

        DecimalFormat df = new DecimalFormat(".000");

        //借助df格式化pi    str="3.142" ,是一个字符串   

        //format()方法,将 double 按指定格式 输出成字符串

        String str = df.format(pi);

        System.out.println("str="+str);

        // 将字符串 转换成 double类型的浮点数

        double result = Double.parseDouble(str);

3.Math.round(a)

4.多重if

范围打乱,顺序对结果又影响 ,要么从大往小写,要么从小往大写,对于顺序打乱,解决办法:加上逻辑限制条件。

5.随机数

[min, max) 整数   (int)(Math.random()*(max-min)+min);

Random  ra  = new Random();

[min, max)    整数:   ra.nextInt(max-min)+min

6.冒泡排序

    int[] arr = {5,4,3,2,1};

            //声明中间变量,用于交换

            int temp=0;

            //控制轮数

            for(int k=0;k

                System.out.println("\n\n第"+(k+1)+"轮开始时:"+Arrays.toString(arr));

                //内层控制的每一轮比较的次数,要保证比较次数在减少, 取值,4,3,2,1

                for(int i=0;i

                    //如果前一个比后一个大需要交换

                    if(arr[i]>arr[i+1]){

                        temp=arr[i];

                        arr[i]= arr[i+1];

                        arr[i+1]=temp;

                    }

                    System.out.println("第"+(k+1)+"轮第"+(i+1)+"次比较:"+Arrays.toString(arr));

                }

                System.out.println("第"+(k+1)+"轮结束后:"+Arrays.toString(arr)+"\n\n");

            }

选择排序

    public static void main(String[] args) {


     int [] arr=new int [] {45,65,32,33,12,1};

     for(int i=0;i

        int index=i;

        for(int j=i+1;j

            if(arr[j]

                int temp=arr[index];

                arr[index]=arr[j];

                arr[j]=temp;

            }

        }


     }

     System.out.println(Arrays.toString(arr));

    }

快速排序


    public static void main(String[] args) {

        int[] arr = new int[] { 1, 2, 4, 5, 7, 4, 5, 3, 9, 0 };

     //System.out.println(Arrays.toString(arr));

        quickSort(arr);

        System.out.println(Arrays.toString(arr));

    }

    private static void quickSort(int[] arr) {

        if (arr.length > 0) {

            quickSort(arr, 0, arr.length - 1);

        }

    }

    private static void quickSort(int[] arr, int low, int high) {

        // 1.递归算法出口

        if (low > high) { // 放在key之前,防止下标越界

            return;

        }

        // 2. 存

        int i = low;

        int j = high;

        // key

        int key = arr[i];

        // 3.完成一趟排序

        while (i < j) {

            // 从右往左找到第一个小于key的数

            while (i < j && arr[j] > key) {

                j--;

            }

            // 从左往右找第一个大于key的数

            while (i < j && arr[i] <= key) {

                i++;

            }

            // 交换

            if (i < j) {

                int temp = arr[i];

                arr[i] = arr[j];

                arr[j] = temp;

            }

        }

        // 当i==j时,调整key的位置

        int p = arr[i];

        arr[i] = arr[low];

        arr[low] = p;

        // 对key左边的数快排

        quickSort(arr, low, i - 1);

        // 对key右边的数快排

        quickSort(arr, i + 1, high);

    }

http://developer.51cto.com/art/201403/430986.htm

7.数组拷贝

       (1) int[] arr = {10,12,13,14,15};

       int[] brr = arr.clone();  //clone()

       (2)System.arraycopy(要拷贝的原始数组arr,原始数组中元素的起始下标从0开始srcfrom,目标数组brr,目标数组中起始位置destfrom,要拷贝的元素个数num) 

     * 需要满足的条件:

     * srcfrom>=0

     * destfrom>=0

     * srcfrom +num <= arr.length

     * destfrom+num<=brr.length

       (3)copyOf(int[] original, int newLength) 

      * copyOf(要拷贝的原始数组,要拷贝的元素个数num)


       (4)copyOfRange(要拷贝的原始数组,拷贝的起始索引从0开始from ,结束索引to)

      * 拷贝的索引范围:[from, to) 左闭右开 ,拷贝的元素个数 to-from


8 .  字符串转数组

(1)使用Java split() 方法

    split() 方法根据匹配给定的正则表达式来拆分字符串。

    注意: . 、 | 和 * 等转义字符,必须得加 \\。多个分隔符,可以用 | 作为连字符。    

    String str = "0,1,2,3,4,5";

    String[] arr = str.split(","); // 用,分割

    System.out.println(Arrays.toString(arr));


` int a=Integer.parseInt(arr[]) String类型转int型`

9。数组转字符串

     String str2 = ArrayUtils.toString(arr, ","); // 数组转字符串(逗号分隔,首尾加大括号)


    String str4 = StringUtils.join(arr, ","); //StringUtils的join方法

10.charAt(2) 取出指定位置的字符

lastIndexOf()最后一个索引的位置

endWith() /startsWith("aa")判断是否以aa开头,返回布尔值

trim() 去掉两边空格

11。 int---》String  Int型转字符串型

    String s1 = String.valueOf(num);

String---》 int   字符串型转int型

    int num2 = Integer.parseInt(s2);

    double num3 =Double.parseDouble(s2);

12 . 截取字符串


//取出10  subString(from,to)  [from,to) 左闭右开

       String num1Str = s.substring(s.indexOf("从")+1, s.indexOf("数到"));

       String num1Str = s.substring(s.indexOf("从")+1, s.indexOf("数到"));

13.//静态变量  ,可以类名.属性名 ,也可以对象名.属性名

//非static变量/普通变量  ,只能通过对象名.属性名来访问

//静态方法ceshi4(),只能调用静态方法ceshi1(),不能调用普通方法ceshi2();

14.//1个类:静态成员变量,实例成员变量(普通的成员变量),静态代码块,普通代码块 ,构造

//执行顺序: 静态成员----》静态代码块 ----》实例成员----》普通代码块----》构造

/

//1.父类的静态成员

//   父类的静态代码块

// 2.子类的静态成员

// 子类的静态代码块

// 3.父类的实例成员

//   父类的普通代码块(非static代码块)

//4.父类的构造Father()

// 5.子类的实例成员

//   子类的普通代码块(非static代码块)

// 6.子类的构造Son()

15.this() :表示调用本类的无参构造

 * this(属性名):表示调用本类的带参构造  ,this(属性1,属性2....) 可以构造的调用,需要放到第1句

16.基本数据类型---》包装类

//直接装箱  int类型的变量,直接赋值给Integer类型的变量 num2

        Integer num2 = num1;

    //int--->Integer

    int num1=10;

    Integer num2 = new Integer(num1);

    Integer num7 = Integer.valueOf(num1);

    //String--->Integer

        String s="10"; 

        Integer num3 = new Integer(s);    

        Integer num8 = Integer.valueOf(s);

17.包装类----》基本数据类型

//直接拆箱   Integer类型变量num2,直接赋值给int类型的变量num3

    int num3 = num2;

     //Integer---》int

     Integer num1 = new Integer(10);

     int num2 = num1.intValue();

    //String--->int

        String s="123";

        int num5=Integer.parseInt(s);

**封装**(降低耦合

将类的信息隐藏在类的内部,对外提供公有的方法,实现对该成员属性的存取操作

封装的好处:隐藏类的实现细节,让使用者使用提供的方法来访问数据,可以方便的加入存取操作,限制不合理的操作

**继承**

一个类可以由其他类派生,子类继承父类特征和方法。

只支持单继承

子类可以继承父类Public和protected修饰的属性和方法

在同一个包中可以继承除private以外的所有修饰的属性和方法

子类无法继承父类的构造方法

子类不能抛出 比父类更多的异常

父类的静态方法不能被子类覆盖为非静态方法,同样父类的非静态方法不能被子类覆盖为静态方法

**继承下构造方法的执行过程**

注意:加载顺序:启动类(java虚拟机启动时,被标明为启动类的类)的static block 最先加载(父类静态成员,静态代码块----子类静态成员,静态代码块---父类实例成员,代码块-----父类构造函数------子类实例成员,代码块---子类构造函数)

*重写*

方法名相同 参数列表相同 返回值类型相同或是其子类 不能缩小被重写方法的访问权限

**final关键字**

final 修饰成员变量,则成为实例常量

final修饰类,类不能被继承

final修饰成员方法,则该方法不能被子类重写。

**super关键字**

在子类构造方法中调用且必须为第一句(调用父类的带参构造方法)super(属性1,属性2..)

使用super关键字 直接调用父类的方法

**多态**

同一引用类型,使用不同的实例而执行不同的操作。

举例说明多态:动物类父类  有“叫”的这样一个动作,  继承它的都是普通类猫 狗等  每个子类叫的方法实现都不一样  ,现在要实现各种动物的叫声  ,如何动态实现  写一个方法把父类做形参传进去

    主人类始终要修改,只要新增宠物子类,主人类就需要添加具体动物看病的方式,子类写不尽的,Host类始终需要修改,不合理 

     *  解决办法:多态来来解决

     *  1.创建Cat子类,extends Pet父类,Cat类中是Cat特有的属性

     *  2. Host类,不需要修改的,只提供一个空方法,带宠物看病

     * public void cure(Pet pet){

     *       pet.toHospital();

     * }

     *  3.父类Pet中,提供1个空方法 ,public  void toHospital(){}

     *  4.每个子类,看病的方式不同,就把各自子类看病的方式,放到各自的子类中去完成

     *   每个子类,去重写父类的toHospital()方法

     *   

     *  5.调用的时候,父类引用,指向子类对象,执行的是各自不同的子类对应的操作

**abstract抽象**

抽象方法(只有方法声明,没有方法实现)*abstract void fun();*

抽象类 :包含抽象方法的类是抽象类;

抽象类不能实例化,可以实例化子类来实现父类的方法

  * 子类必须重写所有抽象方法才能实例化,否则子类还是一个抽象类*

抽象类有构造方法,可以被本类其他构造方法调用,如果不是private修饰,可以被其子类中的构造方法调用。

abstract修饰类和方法,不能修饰属性和构造方法

**接口**

接口中不能定义变量  可以定义常量  自动用public static final修饰  全局静态常量

接口中所有方法都是抽象方法 自动public abstract 修饰

接口不能实例化 不能有构造方法

接口的实现类必须要实现接口的全部方法,除非这个类是抽象类

一个接口不能实现另一个接口,但可以继承多个其他接口

**Date类**

    Date  date=new  Date();

    System.out.println("date="+date);

3个子类构造

    //java.sql.Date  ,默认格式“yyyy-MM-dd"

        Date date = new Date(System.currentTimeMillis());

        // date=2019-03-25

    //java.sql.Time   ,默认格式:“HH:mm:ss”

        Time date2 = new Time(System.currentTimeMillis());

        //date2=11:53:09 

    //java.sql.Timestamp  默认格式“ yyyy-MM-dd HH:mm:ss.SSS" 精确到毫秒

        //1s = 1000ms ,毫秒的范围[000,999]

        Timestamp date3 = new Timestamp(System.currentTimeMillis());

        //date3=2019-03-25 11:54:20.097

DateFormat:日期格式化类。抽象类无法使用

SimpleDateFormat是其子类可以使用

(1 格式化java.util.Date对象  ,让其按指定的格式来显式

     将Date对象 ----format()方法-----》字符串String 来显式

(2 public static String getStrFromDate(Date date,String pattern){

    DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

    Date date = new Date();    

    String result = df.format(date);

    System.out.println("date="+date+",格式化后="+DateUtil.getStrFromDate(date, "yyyy-MM-dd"));


**Calendar类**

Calendar:抽象类,不能直接实例化 ,可以使用其子类 GregorianCalendar

    Calendar cal = new GregorianCalendar();

获取时间

    Calendar cal = Calendar.getInstance();

设置时间

Calendar cal = Calendar.getInstance();

    // 如果想设置为某个日期,可以一次设置年月日时分秒,由于月份下标从0开始赋值月份要-1

    // cal.set(year, month, date, hourOfDay, minute, second);

       cal.set(2018, 1, 15, 23, 59, 59);

**异常**

*  error与exceptionde 区别*

error表示不可处理的异常  通常为内存溢出  jvm崩溃等。

exception表示需要捕捉或者处理的异常

*  throw和throws的区别*

throw在程序中抛出异常,出现在方法体内,如果执行则一定抛出某种异常对象,且只能抛出一个。

throws表示抛出异常的声明,出现在方法头,声明抛出异常的一种可能性,throws后面可以跟多个异常类。

* try catch finally*

try块必须   catch  finally必须出现一个

finally一定会执行,除非System.exit(n)

try块中有return语句,finally语句也会执行,执行return语句会记下返回值,待finally执行结束后,再向调用者返回其值。

*常见的5种RunTimeException*

NullPointerException,空指针异常。

NumberFormatException,数据格式转换错误。

ClassCastException,强制类型转换异常。

IndexOutOfBoundsException,越界异常。

ArithmeticException 算术异常。

除RuntimeException及其子类其他所有的异常都是检查型异常(可查异常)

运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

使用自定义异常的步骤:



1.定义异常类(继承 Throwable 类,Exception类 RuntimeException)。

2.编写构造方法,继承父类的实现。

3.实例化自定义异常对象。

4.使用throw 抛出。

**String、StringBuffer与StringBuilder之间的区别**

1.String含义为引用数据类型,是字符串常量.是不可变的对象,(显然线程安全)在每次对string类型进行改变的时候其实都等同与生成了一个新的String对象.然后指针指向新的String对象,所以经常改变内容的字符串最好不使用String,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了之后.JVM的垃圾回收(GC)就会开始工作,对系统的性能会产生影响

2.StringBuffer  线程安全的可变字符序列:对StringBuffer对象本身进行操作,而不是生成新的对象.所所以在改变对象引用条件下,一般推荐使用StringBuffer.同时主要是使用append和insert方法,

3.StringBuilder 线程不安全的可变字符序列.提供一个与StringBuffer兼容的API,但不同步.设计作为StringBuffer的一个简易替换,用在字符缓冲区被单个线程使用的时候.效率比StringBuffer更快

区别:

a.执行速度:StringBuilder > StringBuffer > String

b.线程安全:StringBuffer线程安全.StringBuilder线程不安全

c.String适用与少量字符串操作

StringBuilder适用单线程下在字符缓冲区下进行大量操作的情况

StringBuffer使用多线程下在字符缓冲区进行大量操作的情况

**集合**

    Collection 接口存储一组不唯一,无序的对象。

    List接口存储一组不唯一,有序,可重复的对象。ArrayList、LinkedList和Vector(淘汰)是主要的实现类

    Set接口存储唯一,无序的对象。HashSet和TreeSet是主要的实现类。

    Map接口存储一组键—值对象,提供Key—Value的映射。其中key列就是一个集合,key不能重复,但是value可以重复。 HashMap、TreeMap和Hashtable是Map的主要实现类。

**Vector扩容机制**

vector 默认的扩容机制是按照容器现有容量的一倍进行增长。由于 Vector 容器分配的是一块连续的内存空间, 每次容器的增长并不是在原有连续的内容空间后进行简单的叠加, 而是重新申请一块更大的新内存, 并把现有容器中的元素逐个复制过去, 然后销毁原有内存。

举例:vector 初始化时申请的空间大小为 6 , 存入了 6 个元素, 当向 vector 中插入第 7 个元素“ 6” 时, vector 会利用自己的扩容机制重新申请空间, 数据存放结构如图 1 所示(_First 指向使用空间的头部,_Last 指向使用空间大小(size)的尾部,_End 指向使用空间容量(capacity)的尾部)。

![](https://i.imgur.com/oHemGds.png)

**ArrayList底层实现原理**

ArrayList的底层数据结构就是一个数组,数组元素的类型为Object类型,对ArrayList的所有操作底层都是基于数组的

ArrayList继承AbstractList抽象父类,实现了List接口(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)。

ArrayList是List接口的可变数组非同步实现,并允许包括null在内的所有元素。

底层使用数组实现

该集合是可变长度数组,数组扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量增长大约是其容量的1.5倍,这种操作的代价很高。

采用了Fail-Fast机制,面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险

remove方法会让下标到数组末尾的元素向前移动一个单位,并把最后一位的值置空,方便GC

(补充)

**LinkedList底层实现原理**

LinkedList是List接口的双向链表非同步实现,并允许包括null在内的所有元素。

底层的数据结构是基于双向链表的,该数据结构我们称为节点

双向链表节点对应的类Node的实例,Node中包含成员变量:prev,next,item。其中,prev是该节点的上一个节点,next是该节点的下一个节点,item是该节点所包含的值。

它的查找是分两半查找,先判断index是在链表的哪一半,然后再去对应区域查找,这样最多只要遍历链表的一半节点即可找到

**HashMap底层实现原理(jdk1.8)**

从结构上讲,hashmap是位桶(Node数组)+链表+红黑树实现的,链表是为了解决hash冲突的,当链表长度超过阈值(8)时,将链表转换为红黑树,大大减少查找时间。

主干是Node数组,包含一个键值对,实现了Map.entry接口,它的初始容量为16, 当链表数组的容量超过初始容量的0.75时,再散列将链表数组扩大2倍,把原链表数组的搬移到新的数组中

*如何getValue*

get方法时获取key的Hash值,通过计算hash&(n-1)得到在链表数组中的位置,判断计算的key与参数key是否相等,不等集遍历后面的链表找到相同的key值返回对应的Value值即可。

*如何put*

判断键值对数组tab[]是否为空或为null,否则以默认大小resize();

根据键值key计算hash值得到插入的数组索引i,如果tab[i]==null,直接新建节点添加

判断当前数组中处理hash冲突的方式为链表还是红黑树(check第一个节点类型即可),分别处理

*HasMap的扩容机制resize();*

构造hash表时,如果不指明初始大小,默认大小为16(即Node数组大小16),如果Node[]数组中的元素达到(填充比*Node.length)重新调整HashMap大小 变为原来2倍大小,扩容很耗时

**Hashtable实现原理**

不允许出现null值null键,线程同步,et/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁,多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,相当于将所有的操作串行化。      线程安全。

**ConcurrentHashMap实现原理**

ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现。

ConcurrentHashMap采用了非常精妙的"分段锁"策略,ConcurrentHashMap的主干是个Segment数组。

     final Segment[] segments;

ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。如果使用传统的技术,如HashMap中的实现,如果允许可以在hash链的中间添加或删除元素,读操作不加锁将得到不一致的数据。ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的。HashEntry代表每个hash链中的一个节点,其结构如下所示

     tatic final class HashEntry {  

     final K key;  

     final int hash;  

     volatile V value;  

     final HashEntry next;  

    } 

可以看到除了value不是final的,其它值都是final的,这意味着不能从hash链的中间或尾部添加或删除节点,因为这需要修改next 引用值,所有的节点的修改只能从头部开始。对于put操作,可以一律添加到Hash链的头部。但是对于remove操作,可能需要从中间删除一个节点,这就需要将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。这在讲解删除操作时还会详述。为了确保读操作能够看到最新的值,将value设置成volatile,这避免了加锁。

**IO操作**

* 1.删除文件及其下面的子目录,子文件,不包括最外层的父文件夹

        public static void delAllFileExceptOuter(File dir){

            if(dir.exists() && dir.isDirectory()){

                //获取子文件列表

                File[] arr = dir.listFiles();

                //遍历

                for(File f:arr){

                    //递归调用自身方法,继续内层的删除

                    delAllFileExceptOuter(f);

                    f.delete();

                }

            }

        }


* 2.删除文件及其下面的子目录,子文件,包括最外层的父文件夹

           public static void delAllFileIncludeOuter(File dir){

            if(dir.exists() && dir.isDirectory()){

                //获取子文件列表

                File[] arr = dir.listFiles();

                //遍历

                for(File f:arr){

                    //递归调用自身方法,继续内层的删除

                    delAllFileIncludeOuter(f);

                    f.delete();

                }

                dir.delete();

            }

        }


* 3.拷贝所有的子文件夹,不拷贝子文件

        public static void copyAllDir(File src,File dest){

            if(src.isDirectory()){

                if(!dest.exists()){

                    dest.mkdirs();

                }


                //获取src的子文件列表 

                File[] arr = src.listFiles();

                //遍历

                for(File f:arr){

                    //构建新的子文件对象  subSrc=new File("E:/others/", c);

                    // subDest = new File("D:/others", c);

                    File subSrc = new File(src,f.getName());

                    File subDest = new File(dest,f.getName());

                    System.out.println("###subSrc="+subSrc+",subDest="+subDest);

                    //递归调用自身方法

                    copyAllDir(subSrc, subDest);

                }

            }


        }

* 4.拷贝单个子文件


        public static void copySingleFile(File src,File dest){

            if(src.isDirectory()){

                System.out.println("只能拷贝文件!");

                return;

            }


        //

        if(dest.isDirectory()){

            System.out.println("要拷贝的文件和父目录中的文件夹重名,不能拷贝!");

            return;

        }


        //声明输入流对象,输出流对象

        FileInputStream fis=  null;

        FileOutputStream fos = null;


        try {

            //建立输入流和源文件之间的联系 

            fis = new FileInputStream(src);

            //建立输出流和目标文件之间的联系

            fos = new FileOutputStream(dest);


            //声明byte[]数组,用来存储读取的内容  ,数组的容量 n:整数值,任意定,项目中一般用1024

            byte[] buffer = new byte[3]; 

            //声明int变量,用来存储read()方法的返回值,实际上存储就是实际读取到的字节个数 

            int len=fis.read(buffer);


            while(len!=-1){

                //读取的内容报错在byte数组中  ,需要转换成String,才能打印输出

//            String data = new String(buffer,0,len);

//                System.out.println("len="+len+"data="+data);


                //读多少,写多少出去

                fos.write(buffer, 0, len);

                //刷新

                fos.flush();

                //接着读

                len=fis.read(buffer);

            }


        } catch (FileNotFoundException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        } finally{

            //释放资源 、关闭流 

            //先打开的后关闭,先创建的后关闭

            if(null!=fos){

                try {

                    fos.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }


            if(null!=fis){

                try {

                    fis.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }


    }

* 5.拷贝所有的子文件,子目录

        public static void copyAllFiles(File src,File dest){

        if(src.getParent()==null && dest.getParent()==null){

            //证明相对路径下,同级之间的拷贝,允许通过

        }else if((src.getParent()==null && dest.getParent()!=null) && (dest.getAbsolutePath().contains(src.getAbsolutePath()))){

            System.out.println("***父目录不能拷贝到子目录中!");

            return;

        }else if(dest.getAbsolutePath().contains(src.getAbsolutePath()) &&  !src.getParent().equals(dest.getParent())){

            //src的parent()和dest的parent()不同

            System.out.println("父目录不能拷贝到子目录中!");

            return;

        }


        if(src.isDirectory()){//如果是目录,拷贝对应的目录

            if(!dest.exists()){

                dest.mkdirs();

            }


            //获取src的子文件列表 

            File[] arr = src.listFiles();

            //遍历

            for(File f:arr){

                //构建新的子文件对象 

                File subDest = new File(dest,f.getName());

                //递归调用自身方法

                copyAllFiles(f, subDest);

            }

        }else if(src.isFile()){ //如果是文件,拷贝文件

            copySingleFile(src, dest);

        }

    }


**IO流**

        * 面试题: 字符串和字节数组,字符串和字符数组之间的相互转换? 

         *  String  s---->byte[]  buffer 

         *  String s="asd";

         *  byte[] buffer=new byte[1024];

         *   buffer = s.getBytes();  

         *  

         *   byte[] buffer ---->String s  

         *s = new String(buffer); 

         *s = new String(buffer,int from, int len);  

         *from:buffer数组的起始索引 

         *len:要转换的字节数 (有几个字节)

         *

         *  

         *  String s ---->char[] buffer 

         *buffer = s.toCharArray();

         *

         *   char[] buffer----->String s  

         *  s = new String(buffer);  

         *  

         *  s= new String(buffer,int from,int len); 

         *  from:buffer字符数组的起始索引

         *  len:要转换的字符个数 (与几个字符)

**IO流文件流FileInputStream/FileOutstream**

       声明输入输入流输出流对象


      FileInputStream fis=null;

      FileOutputStream fos=null;


      建立输入流,输出流与源文件的联系


      fis=new FileInputStream(" ");

      fos=new FileOutputStream(" ");


       声明byte[]字节数组,用于存储读取的内容

       byte[] buffer=new byte[1024];

       int len=0;

       while((len=fis.read(buffer))!=-1){

       String data=new String(buffer,0,len);

       syso(data);

       len=fis.read(buffer,0,buffer.length);


        fos.write(buffer,0,len);

        fos.flush();


       }

拷贝

           //声明输入流对象,输出流对象

        FileReader fr = null;

        FileWriter fw = null;


        try {

            //建立输入流和源文件之间的联系

            fr = new FileReader("E:/others/ceshi.txt");

            //建立输出流和目标文件之间的联系

            fw = new FileWriter("D:/test/ceshi.txt");


            char[] buffer = new char[4];

            //len实际读取的字符数

            int len=0;

            while((len=fr.read(buffer))!=-1){

         //    fw.write(buffer, 0, len);

                //拷贝的时候,一定要带上偏移量

                String s = new String(buffer,0,len);

                fw.write(s);

                fw.flush();

            }

**缓冲流BufferInputStream/BufferOuputStream/BUfferReader/bufferWriter**


     //声明输入流对象,输出流对象

        BufferedReader br = null;

        BufferedWriter bw = null;



        try {

            //建立输入流和源文件之间的联系

            br = new BufferedReader(new FileReader("E:/others/ceshi.txt"));

            //建立输出流和目标文件之间的联系

            bw = new BufferedWriter(new FileWriter("D:/test/ceshi.txt"));


            //声明String变量,用于存储读取的内容 

            String data = null;

            while((data=br.readLine())!=null){

                bw.write(data);

                //刷新

                bw.flush();

                //写一行,换一行

                bw.newLine();

            }

        }

复制图片

    //声明输入流,输出流对象

        BufferedInputStream bis = null;

        BufferedOutputStream bos = null;


        try {

            //建立输入流和源文件之间的联系 

            bis = new BufferedInputStream(new FileInputStream(new File("E:/others/cat.png")));

            //建立输出流和目标文之间的联系

            bos = new BufferedOutputStream(new FileOutputStream(new File("D:/test/cat.png")));

            //声明byte[]数组

            byte[] buffer = new byte[1024];

            //声明int变量

            int len=0;

            while((len=bis.read(buffer))!=-1){

                String s = new String(buffer,0,len);

                System.out.println("len="+len+",s="+s);

                bos.write(buffer, 0, len);

                bos.flush();

            }


**乱码原因**

 * 1.两边的编码方式不一致

 * 2.保存的不完整,数据有丢失

 * InputStreamReader,提供构造,可以传入编码方式

 * InputStreamReader(InputStream in, String charsetName)

          创建使用指定字符集的 InputStreamReader。

 * ANSI ----->程序中gbk

 * 

 * UTF-8----->程序中UTF-8

 * 

 * 乱码因为:ceshi.txt ANSI  ,本地程序中UTF-8,不一致导致的。

 * 

 *    解决办法1:ceshi.txt 右击 另存为 UTF-8

 *    

 *    解决办法2:用InputStreamReader流

    //声明输入流对象

        InputStreamReader isr = null;


        try {

            //建立输入流和源文件之间的联系

            isr = new InputStreamReader(new FileInputStream("E:/others/ceshi.txt"),"gbk");

            //声明char[]数组,用于存储读取的内容

            char[] buffer = new char[4];

            //声明int变量,用来存储实际读取的字符数

            int len = 0;

            while(-1 !=(len=isr.read(buffer))){

                String s = new String(buffer,0,len);

                System.out.println(s);

            }

** FileInputStream与FileReader区别**:

FileInputStream是字节流,FileReader是字符流,用字节流读取中文的时候,可能会出现乱码,而用字符流则不会出现乱码,而且用字符流读取的速度比字节流要快;

**FileInputStream与BufferedInputStream区别**

FileInputStream是字节流,BufferedInputStream是字节缓冲流,使用BufferedInputStream读资源比FileInputStream读取资源的效率高(BufferedInputStream的read方法会读取尽可能多的字节,执行read时先从缓冲区读取,当缓冲区数据读完时再把缓冲区填满。),因此,当每次读取的数据量很小时,FileInputStream每次都是从硬盘读入,而BufferedInputStream大部分是从缓冲区读入。读取内存速度比读取硬盘速度快得多,因此BufferedInputStream效率高,且FileInputStream对象的read方法会出现阻塞;BufferedInputStream的默认缓冲区大小是8192字节。当每次读取数据量接近或远超这个值时,两者效率就没有明显差别了。

**ObjectOutputStream/ObjectInputStream**

java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

**序列化**

只有实现了Serializable(si瑞尔奈zi包)和Externalizable接口的类的对象才能被序列化。

transient修饰属性避免序列化

序列化是指将对象转换成字节序列的过程称为对象的序列化,反序列化则是将字节序列恢复为对象的过程

对象的序列化通常有两种用途

1、把对象的字节序列永久的保存到硬盘上,通常存放到一个文件中

2、在网络上传送对象的序列化

*序列化步骤*

1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;

2) 通过对象输出流的writeObject()方法写对象。

    //声明输出流对象

        ObjectOutputStream oos = null;

        try {

            //建立输出流和目标文件之间的联系  oos需要包装其他的底层流

            oos = new ObjectOutputStream(new FileOutputStream(filePath));

            //调用write()方法写出 

            List list=new ArrayList();

             list.add(new Student("张伟",18));

             list.add(new Student("张伟1",28));

             list.add(new Student("张伟2",38));

            //刷新

             oos.writeObject(list); 

            oos.flush();

        }

*反序列化步骤*

1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;

2) 通过对象输入流的readObject()方法读取对象。

对象序列化和反序列范例:

    // 声明输入流对象

        ObjectInputStream ois = null;

        try {

            // 建立输入流和源文件之间的联系 ,ois需要包装其他的底层流

            ois = new ObjectInputStream(new FileInputStream(filepath));

            // 通过read()方法读取

            List list = (List) ois.readObject();

            for (Student temp : list) {

                System.out.println("取出的学生名:" + temp.getName() + ",年龄:" + temp.getAge() + ",性别:" + temp.getSex());

            }

        }

*s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D​:​*

​字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量

    private static final long serialVersionUID

*显式地定义serialVersionUID有两种用途:*

1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;

2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

**RandomAccessFile类**

RandomAccessFile类的主要功能是完成随机读取功能,可以读取指定位置的内容。

之前的File类只是针对文件本身进行操作的,而如果要想对文件内容进行操作,则可以使用RandomAccessFile类,此类属于随机读取类,可以随机读取一个文件中指定位置的数据。

       * RandomAccessFile:既可以当输出流负责写出,也可以当输入流负责读取

     * 体现它随机访问的特点  seek() skipBytes()

     * 先将3个用户信息,写入到E:/others/user.txt中

     *  然后读取出来

     *  

     *  写入的时候,构造上 ,mode模式选用:rw,文件不存在,则创建

     *  

     *  读取的时候  mode:r 只读

     *  

     *  1.2个方法,一个方法负责写入

     *  一个方法负责读取

     * @author Administrator

     *

     */

    public class TestRandomAccess01 {

        public static void main(String[] args) {

            String path = "E:/others/user.txt";

            save(path);


    //        readFirst(path);

        }


    /**

     * 顺序 ,1,2,3 

     * RandomAccessFile 当 输入流用

     * @param filePath

     */

    public static void readFirst(String filePath){

        //声明输入流对象

        RandomAccessFile ra= null;

        try {

            //建立输入流和源文件之间的联系

            ra = new RandomAccessFile(new File(filePath), "r");

            byte[] buffer = new byte[8];

            for(int i=0;i

                buffer[i] = ra.readByte();

            }

            //读取整数值

            int age = ra.readInt();

            System.out.println("第一个人信息:"+new String(buffer)+"---"+age);


            //读取第2个人  

            for(int i=0;i

                buffer[i] = ra.readByte();

            }

            //读取整数值

            age = ra.readInt();

            System.out.println("第二个人信息:"+new String(buffer)+"---"+age);




            //读取第3个人

            for(int i=0;i

                buffer[i] = ra.readByte();

            }

            //读取整数值

            age = ra.readInt();

            System.out.println("第三个人信息:"+new String(buffer)+"---"+age);


        } catch (FileNotFoundException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        } finally{

            if(ra!=null){

                try {

                    ra.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }


    }


    /**

     * 存储

     * RandomAccessFile 当输出流用

     * @param filePath

     */

    public static void save(String filePath){

        RandomAccessFile ra = null;

        try {

            //建立输出流和目标文件之间的联系,同时指定模式

            ra = new RandomAccessFile(new File(filePath), "rw");


            //调用write()方法写出 

            ra.writeBytes("zhangsan");

            ra.writeInt(30);

            ra.writeBytes("lisi    ");

            ra.writeInt(31);

            ra.writeBytes("wangwu  ");

            ra.writeInt(32);


        } catch (FileNotFoundException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        } finally{

            if(ra!=null){

                try {

                    ra.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }


    }

**线程**

进程:运行的程序,动态。是资源分配的基本单位。 一个进程可拥有多个并行的(concurrent)线程。

线程:进程中一段代码的执行过程。是执行和调度的基本单位。是进程中执行运算单位的最小单位,是进程内部的一个执行单元。

**实现多线程两种方式**

*继承Thread类*

1.定义子类继承Thread类

2.子类重写Thread类run方法

3.创建Thread子类对象,即创建线程对象

4.调用线程对象的start()方法,启动线程

    public class Rabbit extends Thread {

    private int step = 1;

    // (1)线程类中定义一个标志位

    private boolean isRunning = true;

    public Rabbit() {

    }

    public Rabbit(String name) {

        super(name); // 调用父类Thread的带参构造

    }

    @Override

    public void run() {

        // (2)线程体中使用该标志位

        while (isRunning) {

            System.out.println(Thread.currentThread().getName() + "跑了"

                    + (step++) + "步");

        }

    }

    // 提供一个更改此标志位的方法

    public void stopThread() {

        isRunning=false;

    }

    }

    public static void main(String[] args) {

        //新生状态

        Rabbit ra = new Rabbit();

        ra.setName("兔子");

        //就绪状态

        ra.start();

        System.out.println("A判断兔子线程的是否处于活动状态:"+ra.isAlive());

        //延迟2ms 

        try {

            Thread.sleep(2);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        //终止线程 

        //ra.stop();  //可以用,但是不建议用,已过时的方法

        //ra.destroy();//不可以用 ,不起作用

        //(4)外部测试时候,调用更改此标志位的方法

        ra.stopThread();

        try {

            Thread.sleep(2);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("B判断兔子线程的是否处于活动状态:"+ra.isAlive());

    }

*实现Runnable接口*

1.定义子类实现Runnable接口

2.重写Runnable接口run()方法

3.通过Thread含构造器创建线程对象

4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法中

5.调用Thread类的start()方法

优点:1.避免单继承

2.方便共享资源,同一份资源,多个代理访问

**java中终止线程**

*1.使用标志位*

*2.中断策略*

使用标志位这种方法有个很大的局限性,那就是通过循环来使每次的操作都需要检查一下标志位。

java还提供了中断协作机制,能够使一个线程要求另外一个线程停止当前工作。其大致的思想为:调用线程Thread的interrupt([ˌɪntəˈrʌpt])方法,在线程内部通过捕获InterruptedException异常来决定线程是否继续还是退出。如下:

     class InterruptRunnable implements Runnable{

        private BlockingQueue queue = new ArrayBlockingQueue(10);

        @Override

        public void run() {

            int i= 0;

            for (;;) {

                try {

                    //线程的操作

                    i++;

                    queue.put(i);

                } catch (InterruptedException e) {

                    //捕获到了异常 该怎么做

                    System.out.println(queue);

                    e.printStackTrace();

                    return;

                }

            }

          }

      上述代码通过BlockingQueue的put方法来抛出InterruptedException异常。

      当内部捕获到该异常时,从而决定是否继续还是直接退出了

      public void testInterruptRunnable() throws InterruptedException {

        InterruptRunnable runnable = new InterruptRunnable();

        Thread thread = new Thread(runnable);

        thread.start();

        System.err.println(thread.isAlive());

        Thread.sleep(1000);

        thread.interrupt();

        Thread.sleep(1000);

        System.err.println(thread.isAlive());

        Thread.sleep(1000);

        System.err.println(thread.isAlive());

    }

    该测试方法大致同使用标志位的测试方法,同样启动该线程后,1秒后调用线程的interrupt方法,从而触发Runnable的内部queue.put(i)操作抛出InterruptedException异常。

**进程的几种通信方式**

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。

IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

**多线程之线程间的通信方式**

* wait/notify 等待

* Volatile 内存共享

**Yied放弃时间片(暂停线程)**

 * yield: 暂停当前正在执行的线程对象,并执行其他线程

 *  yield:加在哪个线程体里,就暂停谁 ,暂停不一定生效

        class YieldDemo implements Runnable{

        @Override

        public void run() {

            for(int i=0;i<=1000;i++){

                /*if(i%20==0){

                    Thread.yield();  //暂停的线程A

                }*/

                System.out.println(Thread.currentThread().getName()+"----"+i);

            }

        }

        }


          public class TestYield {

          public static void main(String[] args) {

            //创建真实角色类的实例

            YieldDemo yd = new YieldDemo();

            //创建代理角色,代理持有对真实角色的引用

            Thread th = new Thread(yd,"线程A");

            //通过代理开启

            th.start();

            //main()主线程中  

            for(int i=0;i<=1000;i++){

                if(i%20==0){

                    Thread.yield();  //暂停的main(),线程A获得执行的机会

                }

                System.out.println(Thread.currentThread().getName()+"----"+i);

            }

        }

        }



**Join合并线程**

 * join:等待该线程终止。

 * 阻塞线程,合并线程

 * join()阻塞自身,让其他线程获得执行的机会,等其他线程执行完毕,自身才接着执行 。

 * 加在哪个线程体里,就阻塞谁


           class JoinDemo implements Runnable{

            @Override

            public void run() {

                for(int i=0;i<=1000;i++){

                    System.out.println(Thread.currentThread().getName()+"----"+i);

                }

            }

         }



         public class TestJoin {

            public static void main(String[] args) {

                //创建真实角色

                JoinDemo jd = new JoinDemo();

                //创建代理,代理持有对真实角色的引用

                Thread th = new Thread(jd,"线程a");

                //通过代理启动

                th.start();


            for(int i=0;i<=1000;i++){

                if(i==50){

                    try {

                        th.join(); //阻塞main线程

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    } 

                }

                System.out.println(Thread.currentThread().getName()+"----"+i);

            }

        }

        }


**Synchronized线程的同步与锁**

* Synchronized允许加在方法前,表示方法是线程安全的。

* 多个代理访问同一份资源,出现资源抢夺的问题,同步 :并发,多个线程访问同一份资源,确保资源安         全----线程安全

* 方式一:同步代码块

synchronized(引用类型、this/类.class){  ..... }

注意:使用实现Runnable接口方式创建多线程,同步代码块中的锁可以用this,如何使继承Thread类,慎this

* 方式二:同步方法

访问修饰符  synchornized 返回值类型 方法名 (){...}

**单例模式**

单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。

关键点:

1)一个类只有一个实例       这是最基本的

2)它必须自行创建这个实例

3)它必须自行向整个系统提供这个实例

两种实现方式:

*懒汉模式*

(类加载时不初始化)

     public class LazySingleton {

    //懒汉式单例模式

    //比较懒,在类加载时,不创建实例,因此类加载速度快,但运行时获取对象的速度慢



    private static LazySingleton intance = null;//静态私用成员,没有初始化


    private LazySingleton()

    {

        //私有构造函数

    }


    public static synchronized LazySingleton getInstance()    //静态,同步,公开访问点

    {

        if(intance == null)

        {

            intance = new LazySingleton();

        }

        return intance;

    }

    }

*饿汉模式*

(在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快)

     public class EagerSingleton {

    //饿汉单例模式

    //在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快


    private static EagerSingleton instance = new EagerSingleton();//静态私有成员,已初始化

    private EagerSingleton() 

    {

        //私有构造函数

    }

    public static EagerSingleton getInstance()    //静态,不用同步(类加载时已初始化,不会有多线程的问题)

    {

        return instance;

    }

    }

**wait()和notify()**

 * wait()方法:调用wait()方法,会挂起当前线程,并释放共享资源的锁.

* notify()方法:调用了任意对象的notify()方法会在因调用该对象的wait()方法而阻塞的线程中随机选择一个解除阻塞,但要等到获得锁后才可真正执行。

* notifyAll()方法:调用了notifyAll()方法会将因调用该对象的wait()方法而阻塞的所有线程一次性全部解除阻塞。

* wait(),notify(),和notifyall()这3个方法都是Object类中的final方法,被所有的类继承且不允许重写。这3个方法只能在同步方法或同步代码块中使用,否则会抛出异常。

**wait()方法和sleep()方法  区别**

* wait() :线程进入等待状态,不占用任何资源,不增加时间限制,因为wait方法会释放锁,所以调用该方法时,要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。

* sleep():线程睡眠,线程被调用时,占着cpu不工作,消耗内存资源,增加时间限制

,必须捕获异常

**同步和异步的区别**

同步是指两个线程的运行是相关的,其中一个线程要阻塞等待另外一个线程的运行。异步的意思是两个线程毫无相关,自己运行自己的。

以通讯为例

          同步:发送一个请求,等待返回,然后再发送下一个请求 

异步:发送一个请求,不等待返回,随时可以再发送下一个请求

          并发:同时发送多个请求

**socket编程**

套接字使用Tcp提供了两台计算机之间的通信机制

区分不同应用程序进程之间的网络通信和连接

ServerSocket用于服务器端,通过accept()监听请求,然后返回Socket。

Socket用于客户端

**TCP和UDP的区别**

Tcp提供面向连接的,可靠的字节流传输,并且提供了拥塞控制和流量控制机制

UDP提供面向无连接的,不可靠的数据报的传输,不提供拥塞控制和流量控制机制

**Socket通信实现步骤:**

简化出Socket通信的实现步骤:

1.创建ServerSocket和Socket,建立连接

2.打开链接到Socket的输入/输出流

3.按照协议对Socket进行读/写操作

4.关闭输入输出流、关闭Socket

*使用多线程实现多客户端的通信:*

多线程基本步骤:

1.服务器端创建ServerSocket,循环调用accept()等待客户端连接。

2.客户端创建一个socket并请求和服务器端连接。

3.服务器端接收客户端请求,创建socket与该客户建立专线连接。

4.建立连接的两个socket在一个单独的线程上对话。

5.服务器端继续等待新的连接

**NIO主要原理及使用**

NIO采取通道(Channel)和缓冲区(Buffer)来传输和保存数据,它是非阻塞式的I/O,即在等待连接、读写数据(这些都是在一线程以客户端的程序中会阻塞线程的操作)的时候,程序也可以做其他事情,以实现线程的异步操作。

考虑一个即时消息服务器,可能有上千个客户端同时连接到服务器,但是在任何时刻只有非常少量的消息需要读取和分发(如果采用线程池或者一线程一客户端方式,则会非常浪费资源),这就需要一种方法能阻塞等待,直到有一个信道可以进行I/O操作。NIO的Selector选择器就实现了这样的功能,一个Selector实例可以同时检查一组信道的I/O状态,它就类似一个观察者,只要我们把需要探知的SocketChannel告诉Selector,我们接着做别的事情,当有事件(比如,连接打开、数据到达等)发生时,它会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的SocketChannel,然后,我们从这个Channel中读取数据,接着我们可以处理这些数据。

Selector内部原理实际是在做一个对所注册的Channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个Channel有所注册的事情发生,比如数据来了,它就会读取Channel中的数据,并对其进行处理。

要使用选择器,需要创建一个Selector实例,并将其注册到想要监控的信道上(通过Channel的方法实现)。最后调用选择器的select()方法,该方法会阻塞等待,直到有一个或多个信道准备好了I/O操作或等待超时,或另一个线程调用了该选择器的wakeup()方法。现在,在一个单独的线程中,通过调用select()方法,就能检查多个信道是否准备好进行I/O操作,由于非阻塞I/O的异步特性,在检查的同时,我们也可以执行其他任务。

基于NIO的TCP连接的建立步骤

服务端

    1、传建一个Selector实例;

    2、将其注册到各种信道,并指定每个信道上感兴趣的I/O操作;

    3、重复执行:

        1)调用一种select()方法;

        2)获取选取的键列表;

        3)对于已选键集中的每个键:

           a、获取信道,并从键中获取附件(如果为信道及其相关的key添加了附件的话);

           b、确定准备就绪的操纵并执行,如果是accept操作,将接收的信道设置为非阻塞模式,并注册到选择器;

           c、如果需要,修改键的兴趣操作集;

           d、从已选键集中移除键

客户端

与基于多线程的TCP客户端大致相同,只是这里是通过信道建立的连接,但在等待连接建立及读写时,我们可以异步地执行其他任务。

**double转byte类型**

    private static byte[] data;

    public static byte[] convert(double num) throws IOException {

        data = null;

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        DataOutputStream dos = new DataOutputStream(bos);

        dos.writeDouble(num);

        dos.flush();

        data = bos.toByteArray();

        dos.close();

        return data;

    }

**byte转double类型**

    public static double convert(byte[] data) throws IOException {

        DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));

        double num = dis.readDouble();

        dis.close();

        return num;

    }

**UDP通信**

*发送引用数据类型*

*    客户端:

1)    创建客户端 DatagramSocket类 +指定端口

2)    准备数据  字节数组

3)    打包 DatagramPacket +服务器地址及端口

4)    发送

5)    释放资源

*    服务器:

1)    创建服务端DatagramSocket类+指定端口

2)    准备接受容器  字节数组 封装DatagramPacket(封装成包)

3)    包 接收数据

4)    分析

5)    释放资源

*发送基本数据类型*

*    客户端:


1)    创建客户端 DatagramSocket类 +指定端口

2)    准备数据 基本数据类型转换成字节数组(字节数组输出流ByteArrayOutputStream toByteArray 数据字节输出流DataOutputStream)

3)    打包 DatagramPacket +服务器地址及端口(发送的地点以及端口)

4)    发送

5)    释放资源

*    服务器:

1)    创建服务端DatagramSocket类+指定端口

2)    准备接受容器  字节数组 封装DatagramPacket(封装成包)

3)    包 接收数据

4)    分析数据 字节数组转换成基本数据类型(字节数组输入流ByteArrayOutputStream 数据字节输入流DataInputStream)

5)    释放资源

你可能感兴趣的:(JAVASE学习笔记)