Java 基础语法

String

String 基础

java 中字符的编码为 Unicode
String 前缀定义字符串
字符 + 数字 会将数字转化为 字符串拼接

String类和常量池

常量池

String str1 = "abc";
String str2 = new String("def");
String str3 = "abc";
String str4 = str2.intern();
String str5 = "def";
System.out.println(str1 == str3);// true
System.out.println(str2 == str4);// false
System.out.println(str4 == str5);// true

  • str1 首先堆中会有一个 "abc" 的实例,全局变量 String Pool 中存放在 "abc" 的一个引用值
  • str2 会生成两个实力,一个是 "def" 的实例对象,并且String Pool中存储一个“def”的引用值,还有一个是 new 出来的一个 "def" 的实例对象
  • str3 在解析的时候,发现 String Pool 里面存在 "abc"的全局驻留字符串引用,所以 str3 直接引用这个地址
  • str4 调用了 intern() 函数,会返回 String Pool 中 "def" 的引用值
  • str5 解析的时候 就直接能在 String Pool 中找到对应的 "def" 的引用 这个时候 str4 和 str5 就是相等的

String 类的常用的方法

public class StringTest1 {
    public static void main(String[] args) {
        String s1 = "core Java";
        String s2 = "Core Java";
        System.out.println(s1.charAt(3));//提取下标为3的字符
        System.out.println(s2.length());//字符串的长度
        System.out.println(s1.equals(s2));//比较两个字符串是否相等
        System.out.println(s1.equalsIgnoreCase(s2));//比较两个字符串(忽略大小写)
        System.out.println(s1.indexOf("Java"));//字符串s1中是否包含Java
        System.out.println(s1.indexOf("apple"));//字符串s1中是否包含apple
        String s = s1.replace(' ', '&');//将s1中的空格替换成&
        System.out.println("result is :" + s);
    }
}

运行结果

字符串相等的判断

判断字符串 内容是否相等 不能用'=='
要使用 .equals() 来比较
'==' 这个比较 还会验证是否执行同一个 字符串常量对象

例:

public class TestStringEquals {
    public static void main(String[] args) {
        String g1 = "北京尚学堂";
        String g2 = "北京尚学堂";
        String g3 = new String("北京尚学堂");
        System.out.println(g1 == g2); // true  指向同样的字符串常量对象
        System.out.println(g1 == g3); // false  g3是新创建的对象
        System.out.println(g1.equals(g3)); // true  g1和g3里面的字符串内容是一样的
    }
}

实际分配地址的 图示


  • 异常处理

    异常的英文:Exception

    异常处理的 基本格式

    try{
        xxxxxxx
    } catch(Exception e){
        e.xxxx();
    }
    

    try-catch-finally

    异常分类

    Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception。

    Error

    程序无法处理的错误
    表明系统 JVM 已经处于不可恢复的崩溃状态中

    Exception

    异常处理
    空指针异常(NullPointerException)、数组下标越界异常(ArrayIndexOutOfBoundsException)、类型转换异常(ClassCastException)、算术异常(ArithmeticException)等。

    异常捕获

    try 运行的话
    catch 捕捉
    finally 执行所以代码都要执行 异常处理的出口

    注意

    即使try和catch块中存在return语句,finally语句也会执行。是在执行完finally语句后再通过return退出。    2. finally语句块只有一种情况是不会执行的,那就是在执行finally之前遇到了System.exit(0)结束程序运行。

    自定义异常

    1. 从Exception类 或者 它的子类派生一个子类
    2. 可以继承 Exception 类 然后利用构造函数构造

数组

数组声明

type[] arr_name; 一般使用的方法
第一次这样 只是声明了数组,但是还没有实例化(分配空间)

基本类型申请

public class Test {
    public static void main(String args[]) {
        int[] s = null; // 声明数组;
        s = new int[10]; // 给数组分配空间;
        for (int i = 0; i < 10; i++) {
            s[i] = 2 * i + 1;//给数组元素赋值;
            System.out.println(s[i]);
        } 
    }
}

引用类型 一位数组

class Man{
    private int age;
    private int id;
    public Man(int id,int age) {
        super();
        this.age = age;
        this.id = id;
    }
}
public class AppMain {
    public static void main(String[] args) {
        Man[] mans;  //声明引用类型数组; 
        mans = new Man[10];  //给引用类型数组分配空间;
         
        Man m1 = new Man(1,11);
        Man m2 = new Man(2,22);  
         
        mans[0]=m1;//给引用类型数组元素赋值;
        mans[1]=m2;//给引用类型数组元素赋值;
    }
}

初始化

静态初始化
在定义数组的时候 直接赋值

int[] a = { 1, 2, 3 };// 静态初始化基本类型数组;
Man[] mans = { new Man(1, 1), new Man(2, 2) };// 静态初始化引用类型数组;

动态初始化
分配空间和 赋值分开的

int[] a1 = new int[2];//动态初始化数组,先分配空间;
a1[0]=1;//给数组元素赋值;
a1[1]=2;//给数组元素赋值;

默认初始值
整数数组的初始值:0
boolean的初始值:false
String字符串的初始值:null

遍历数组

可以 根据数组下标 遍历

public class Test {
    public static void main(String[] args) {
        int[] a = new int[4];
        //初始化数组元素的值
        for(int i=0;i<a.length;i++){
            a[i] = 100*i;
        }
        //读取元素的值
        for(int i=0;i<a.length;i++){
            System.out.println(a[i]);
        }
    }
}

直接取值

public class Test {
    public static void main(String[] args) {
        String[] ss = { "aa", "bbb", "ccc", "ddd" };
        for (String temp : ss) {
            System.out.println(temp);
        }
    }
}

打印数组

用到System.out.println(Arrays.toString(数组名));// 打印数组元素
如果直接 System.out.println(a); //打印数组的引用

二分法查找

import java.util.Arrays;
public class Test {
    public static void main(String[] args) {
        int[] arr = { 30,20,50,10,80,9,7,12,100,40,8};
        int searchWord = 20; // 所要查找的数
        Arrays.sort(arr); //二分法查找之前,一定要对数组元素排序
        System.out.println(Arrays.toString(arr));
        System.out.println(searchWord+"元素的索引:"+binarySearch(arr,searchWord));
    }
 
    public static int binarySearch(int[] array, int value){
        int low = 0;
        int high = array.length - 1;
        while(low <= high){
            int middle = (low + high) / 2;
            if(value == array[middle]){
                return middle;         //返回查询到的索引位置
            }
            if(value > array[middle]){
                low = middle + 1;
            }
            if(value < array[middle]){
                high = middle - 1;
            }
        }
        return -1;     //上面循环完毕,说明未找到,返回-1
    }
}

常用函数等

装包

public class WrapperClassTest {
    public static void main(String[] args) {
        Integer i = new Integer(10);
        Integer j = new Integer(50);
    }
}

自动装箱 拆箱

现在的 编译器会 自动帮忙执行 装箱和拆箱

自动装箱

Integer i = 100;//自动装箱//相当于编译器自动为您作以下的语法编译:
Integer i = Integer.valueOf(100);//调用的是valueOf(100),而不是new Integer(100)

自动拆箱

Integer i = 100;
int j = i;//自动拆箱//相当于编译器自动为您作以下的语法编译:int j = i.intValue();

包的缓存大小

缓存 -128~127(可以用 == 号进行比较) 大小 如果超出了 不会让相同内容执行同一地址引用

public class Test3 {
    public static void main(String[] args) {
        Integer in1 = -128;
        Integer in2 = -128;
        System.out.println(in1 == in2);//true 因为123在缓存范围内
        System.out.println(in1.equals(in2));//true
        Integer in3 = 1234;
        Integer in4 = 1234;
        System.out.println(in3 == in4);//false 因为1234不在缓存范围内
        System.out.println(in3.equals(in4));//true
    }
}

示意图:

String 类

定义的 String 是不可以修改的 因为源码定义的时候 加了 final 修饰

在 String 类中的函数对 字符串 进行操作,不是对源字符串而是生成了新的对象

注意: haseCode 相同引用的值 不一定相同

String常用函数

  1. 返回新String对象:concat() , replace() , substring(), toLowerCase() 转化为全小写 , toUpperCase() 转化为大写 , trim()
  2. 查询 endsWith() , startWith() , indexOf() , lastIndexOf()
  3. 比较 equals() , equalsIgnoreCase() , compareTo()

StringBuffer 和 StringBuilder

可变字符序列 (源码没用 用final修饰)

常用指令

  1. 重载的public StringBuilder append(…)方法        可以为该StringBuilder 对象添加字符序列,仍然返回自身对象。
  2. 方法 public StringBuilder delete(int start,int end)        可以删除从start开始到end-1为止的一段字符序列,仍然返回自身对象。
  3. 方法 public StringBuilder deleteCharAt(int index)        移除此序列指定位置上的 char,仍然返回自身对象。
  4. 重载的public StringBuilder insert(…)方法        可以为该StringBuilder 对象在指定位置插入字符序列,仍然返回自身对象。
  5. 方法 public StringBuilder reverse()       用于将字符序列逆序,仍然返回自身对象。
  6. 方法public String toString()返回此序列中数据的字符串表示形式。
    7. 和 String 类含义类似的方法:
public class TestStringBufferAndBuilder 1{
    public static void main(String[] args) {
        /**StringBuilder*/
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 7; i++) {
            sb.append((char) ('a' + i));//追加单个字符
        }
        System.out.println(sb.toString());//转换成String输出
        sb.append(", I can sing my abc!");//追加字符串
        System.out.println(sb.toString());
        /**StringBuffer*/
        StringBuffer sb2 = new StringBuffer("中华人民共和国");
        sb2.insert(0, "爱").insert(0, "我");//插入字符串
        System.out.println(sb2);
        sb2.delete(0, 2);//删除子字符串
        System.out.println(sb2);
        sb2.deleteCharAt(0).deleteCharAt(0);//删除某个字符
        System.out.println(sb2.charAt(0));//获取某个字符
        System.out.println(sb2.reverse());//字符串逆序
    }
}

String, StringBuffer 和 StringBuilder

在对 String 进行操作会返回 一个新的内存 之前的 会被放弃
从而在进行大量的操作的时候 会影响到程序的运行
所以一般会用 StringBufferStringBuilder 代替 String 从而对 字符串进行操作

测试:

public class Test {
    public static void main(String[] args) {
        /**使用String进行字符串的拼接*/
        String str8 = "";
        //本质上使用StringBuilder拼接, 但是每次循环都会生成一个StringBuilder对象
        long num1 = Runtime.getRuntime().freeMemory();//获取系统剩余内存空间
        long time1 = System.currentTimeMillis();//获取系统的当前时间
        for (int i = 0; i < 5000; i++) {
            str8 = str8 + i;//相当于产生了10000个对象
        }
        long num2 = Runtime.getRuntime().freeMemory();
        long time2 = System.currentTimeMillis();
        System.out.println("String占用内存 : " + (num1 - num2));
        System.out.println("String占用时间 : " + (time2 - time1));
        /**使用StringBuilder进行字符串的拼接*/
        StringBuilder sb1 = new StringBuilder("");
        long num3 = Runtime.getRuntime().freeMemory();
        long time3 = System.currentTimeMillis();
        for (int i = 0; i < 5000; i++) {
            sb1.append(i);
        }
        long num4 = Runtime.getRuntime().freeMemory();
        long time4 = System.currentTimeMillis();
        System.out.println("StringBuilder占用内存 : " + (num3 - num4));
        System.out.println("StringBuilder占用时间 : " + (time4 - time3));
    }
}

要点:

  1. String:不可变字符序列。
  2. StringBuffer:可变字符序列,并且线程安全,但是效率低。
  3. StringBuilder:可变字符序列,线程不安全,但是效率高(一般用它)。

文件操作

文件创建

import java.io.File;
public class TestFile1 {
    public static void main(String[] args) throws Exception {
        System.out.println(System.getProperty("user.dir"));
        File f = new File("a.txt"); //相对路径:默认放到user.dir目录下面
        f.createNewFile();//创建文件
        File f2 = new File("d:/b.txt");//绝对路径
        f2.createNewFile();
    }
}

文件属性判断

 

import java.io.File;
import java.util.Date;
public class TestFile2 {
    public static void main(String[] args) throws Exception {
        File f = new File("d:/b.txt");
        System.out.println("File是否存在:"+f.exists());
        System.out.println("File是否是目录:"+f.isDirectory());
        System.out.println("File是否是文件:"+f.isFile());
        System.out.println("File最后修改时间:"+new Date(f.lastModified()));
        System.out.println("File的大小:"+f.length());
        System.out.println("File的文件名:"+f.getName());
        System.out.println("File的目录路径:"+f.getPath());
    }
}

测试

目录操作

 

枚举

格式

enum  枚举名 {
      枚举体(常量列表)
}

默认为 public static final 修饰


容器

泛型

帮助我们 建立类型安全的集合。
在使用了泛型的集合中,遍历时不比进行强制类型转换

泛型自定义

在类的声明出增加泛型列表 <T, E, V>

声明
class MyCollection<E> {// E:表示泛型;
    Object[] objs = new Object[5];
 
    public E get(int index) {// E:表示泛型;
        return (E) objs[index];
    }
    public void set(E e, int index) {// E:表示泛型;
        objs[index] = e;
    }
}

应用
public class TestGenerics {
    public static void main(String[] args) {
        // 这里的”String”就是实际传入的数据类型;
        MyCollection<String> mc = new MyCollection<String>();
        mc.set("aaa", 0);
        mc.set("bbb", 1);
        String str = mc.get(1); //加了泛型,直接返回String类型,不用强制转换;
        System.out.println(str);
    }
}

使用泛型能使代码变得灵活多变
CollectionListSetMap、Iterator接口都定义了泛型

Collection 接口

Collection接口的两个子接口使List, Set

接口中定义的方法

List特点和常用方法

有序可重复容器

  • 有序:List 中每个元素都有索引标号
  • 可重复:List 允许加入重复的元素

接口中定义的方法

常用
ArrayList, LinkedList , Vector

public class TestList {
    /**
     * 测试add/remove/size/isEmpty/contains/clear/toArrays等方法
     */
    public static void test01() {
        List<String> list = new ArrayList<String>();
        System.out.println(list.isEmpty()); // true,容器里面没有元素
        list.add("高淇");
        System.out.println(list.isEmpty()); // false,容器里面有元素
        list.add("高小七");
        list.add("高小八");
        System.out.println(list);
        System.out.println("list的大小:" + list.size());
        System.out.println("是否包含指定元素:" + list.contains("高小七"));
        list.remove("高淇");
        System.out.println(list);
        Object[] objs = list.toArray();
        System.out.println("转化成Object数组:" + Arrays.toString(objs));
        list.clear();
        System.out.println("清空所有元素:" + list);
    }
    public static void main(String[] args) {
        test01();
    }
}

 

元素处理

public class TestList {
    public static void main(String[] args) {
        test02();
    }
    /**
     * 测试两个容器之间元素处理
     */
    public static void test02() {
        List<String> list = new ArrayList<String>();
        list.add("高淇");
        list.add("高小七");
        list.add("高小八");
 
        List<String> list2 = new ArrayList<String>();
        list2.add("高淇");
        list2.add("张三");
        list2.add("李四");
        System.out.println(list.containsAll(list2)); //false list是否包含list2中所有元素
        System.out.println(list);
        list.addAll(list2); //将list2中所有元素都添加到list中
        System.out.println(list);
        list.removeAll(list2); //从list中删除同时在list和list2中存在的元素
        System.out.println(list);
        list.retainAll(list2); //取list和list2的交集
        System.out.println(list);
    }
}

 

Map接口

Map就是用来存储“键(key)-值(value) 对”的

HashMap 和 HashTable

采用 哈希算法实现,是 Map 接口最常用的实现类
要求 HashMap 中我们的 键不能重复,不然新键值会替换旧的

public class TestMap {
    public static void main(String[] args) {
        Map<Integer, String> m1 = new HashMap<Integer, String>();
        Map<Integer, String> m2 = new HashMap<Integer, String>();
        m1.put(1, "one");
        m1.put(2, "two");
        m1.put(3, "three");
        m2.put(1, "一");
        m2.put(2, "二");
        System.out.println(m1.size());
        System.out.println(m1.containsKey(1));
        System.out.println(m2.containsValue("two"));
        m1.put(3, "third"); //键重复了,则会替换旧的键值对
        Map<Integer, String> m3 = new HashMap<Integer, String>();
        m3.putAll(m1);
        m3.putAll(m2);
        System.out.println("m1:" + m1);
        System.out.println("m2:" + m2);
        System.out.println("m3:" + m3);
    }
}

hashmap 结构

是一个单向链表结构


 

  • IO技术

    基本概念

    程序 输入输出的时候 数据传输

    读取文件内容

    代码

    import java.io.*;
    public class TestIO2 {
        public static void main(String[] args) {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream("d:/a.txt"); // 内容是:abc
                StringBuilder sb = new StringBuilder();
                int temp = 0;
                //当temp等于-1时,表示已经到了文件结尾,停止读取
                while ((temp = fis.read()) != -1) {
                    sb.append((char) temp);
                }
                System.out.println(sb);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    //这种写法,保证了即使遇到异常情况,也会关闭流对象。
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

     

    Java 中流的分类

     

    输入流 输出流

    **输入流:**数据流向程序
    **输出流:**数据流向目的地

    字节流 字符流

    **字节流:**以字节为单位获取
    **字符流:**以字符为单位获取

    节点流 处理流

    **节点流:**直接从数据源或目的地读写数据
    **处理流:**不直接连接到数据源或目的地,是”处理流的流”。通过对其他流的处理提高程序的性能

    四大IO抽象类

    InputStream

    此抽象类是表示字节输入流的所有类的父类。不可以实例化,数据读取考它子类来实现

    OutputStream

    此抽象类是表示字节输出流的所有类的父类。输出流接收输出字节并将这些字节发送到某个目的地。

    Reader

    用于读取的字符流抽象类,数据单位为字符

    Write

    用于写入字符流抽象类,数据单位为字符

    文件字节流

    FilelnputStream 通过字节的方式读取文件,适合读取所有类型的文件
    FileOutputStream 通过字节的方式写数据到文件中,适合所有类型的文件

    将字符串/字节数组的内容写入到文件中

    import java.io.FileOutputStream;
    import java.io.IOException;
    public class TestFileOutputStream {
        public static void main(String[] args) {
            FileOutputStream fos = null;
            String string = "北京尚学堂欢迎您!";
            try {
                // true表示内容会追加到文件末尾;false表示重写整个文件内容。
                fos = new FileOutputStream("d:/a.txt", true);
                //该方法是直接将一个字节数组写入文件中; 而write(int n)是写入一个字节
                fos.write(string.getBytes());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    方法介绍:
    用到一个write方法:void write(byte[ ] b),该方法不再一个字节一个字节地写入,而是直接写入一个字节数组;另外其还有一个重载的方法:void write(byte[ ] b, int off, int length),这个方法也是写入一个字节数组,但是我们程序员可以指定从字节数组的哪个位置开始写入,写入的长度是多少。

    利用文件流实现文件复制

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    public class TestFileCopy {
        public static void main(String[] args) {
            //将a.txt内容拷贝到b.txt
            copyFile("d:/a.txt", "d:/b.txt"); 
        }
     
        /**
         * 将src文件的内容拷贝到dec文件
         * @param src 源文件
         * @param dec 目标文件
         */
        static void copyFile(String src, String dec) {
            FileInputStream fis = null;
            FileOutputStream fos = null;
            //为了提高效率,设置缓存数组!(读取的字节数据会暂存放到该字节数组中)
            byte[] buffer = new byte[1024];
            int temp = 0;
            try {
                fis = new FileInputStream(src);
                fos = new FileOutputStream(dec);
                //边读边写
                //temp指的是本次读取的真实长度,temp等于-1时表示读取结束
                while ((temp = fis.read(buffer)) != -1) {
                    /*将缓存数组中的数据写入文件中,注意:写入的是读取的真实长度;
                     *如果使用fos.write(buffer)方法,那么写入的长度将会是1024,即缓存
                     *数组的长度*/
                    fos.write(buffer, 0, temp);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //两个流需要分别关闭
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    a.txt 的内容 写道 b.txt 中去

    文件字符流

    字符流 不能很好的队 Unicode 字符 进行处理

    使用FileReader 与 FileWriter 实现文件复制

    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    public class TestFileCopy2 {
        public static void main(String[] args) {
            // 写法和使用Stream基本一样。只不过,读取时是读取的字符。
            FileReader fr = null;
            FileWriter fw = null;
            int len = 0;
            try {
                fr = new FileReader("d:/a.txt");
                fw = new FileWriter("d:/d.txt");
                //为了提高效率,创建缓冲用的字符数组
                char[] buffer = new char[1024];
                //边读边写
                while ((len = fr.read(buffer)) != -1) {
                    fw.write(buffer, 0, len);
                }
     
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fw != null) {
                        fw.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (fr != null) {
                        fr.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    缓冲字节流

    缓冲流。为了提高程序 读写信息的能力,缓冲流是先将数据缓存起来,当缓存区存满后或者手动刷新时再一次性的读取到程序或写入目的地

    BufferedInputStreamBufferedOutputStream这两个流是缓冲字节流

    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
     
    public class TestBufferedFileCopy1 {
     
        public static void main(String[] args) {
            // 使用缓冲字节流实现复制
            long time1 = System.currentTimeMillis();
            copyFile1("D:/电影/华语/大陆/尚学堂传奇.mp4", "D:/电影/华语/大陆/尚学堂越
                     "+"来越传奇.mp4");
            long time2 = System.currentTimeMillis();
            System.out.println("缓冲字节流花费的时间为:" + (time2 - time1));
     
            // 使用普通字节流实现复制
            long time3 = System.currentTimeMillis();
            copyFile2("D:/电影/华语/大陆/尚学堂传奇.mp4", "D:/电影/华语/大陆/尚学堂越
                     "+"来越传奇2.mp4");
            long time4 = System.currentTimeMillis();
            System.out.println("普通字节流花费的时间为:" + (time4 - time3));
        }
        /**缓冲字节流实现的文件复制的方法*/
        static void copyFile1(String src, String dec) {
            FileInputStream fis = null;
            BufferedInputStream bis = null;
            FileOutputStream fos = null;
            BufferedOutputStream bos = null;
            int temp = 0;
            try {
                fis = new FileInputStream(src);
                fos = new FileOutputStream(dec);
                //使用缓冲字节流包装文件字节流,增加缓冲功能,提高效率
                //缓存区的大小(缓存数组的长度)默认是8192,也可以自己指定大小
                bis = new BufferedInputStream(fis);
                bos = new BufferedOutputStream(fos);
                while ((temp = bis.read()) != -1) {
                    bos.write(temp);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //注意:增加处理流后,注意流的关闭顺序!“后开的先关闭!”
                try {
                    if (bos != null) {
                        bos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (bis != null) {
                        bis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        /**普通节流实现的文件复制的方法*/
        static void copyFile2(String src, String dec) {
            FileInputStream fis = null;
            FileOutputStream fos = null;
            int temp = 0;
            try {
                fis = new FileInputStream(src);
                fos = new FileOutputStream(dec);
                
                while ((temp = fis.read()) != -1) {
                    fos.write(temp);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    结果图:

    序列化和反序列化

    把java 对象转换位字节序列的过程称为对象的序列化。把1字节序列恢复为 java 对象的过程称为对象的反序列化

    分类:

    1. 持久化:把对象的字节序列永久1的保存到硬盘上,通常放到一个文件中
    2. 网路通信:在网络上传送对象的字节序列。

    序列化涉及的类和接口

    • ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
    • ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
    • 只有实现了Serializable接口的类的对象才能被序列化。 Serializable接口是一个空接口,只起到标记作用。

    序列化/反序列化的布置和实例

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
     
    //Person类实现Serializable接口后,Person对象才能被序列化class Person implements Serializable {
        // 添加序列化ID,它决定着是否能够成功反序列化!
        private static final long serialVersionUID = 1L;
        int age;
        boolean isMan;
        String name;
     
        public Person(int age, boolean isMan, String name) {
            super();
            this.age = age;
            this.isMan = isMan;
            this.name = name;
        }
     
        @Override
        public String toString() {
            return "Person [age=" + age + ", isMan=" + isMan + ", name=" + name + "]";
        }
    }
     
    public class TestSerializable {
        public static void main(String[] args) {
            FileOutputStream fos = null;
            ObjectOutputStream oos = null;
            ObjectInputStream ois = null;
            FileInputStream fis = null;
            try {
                // 通过ObjectOutputStream将Person对象的数据写入到文件中,即序列化。
                Person person = new Person(18, true, "高淇");
                // 序列化
                fos = new FileOutputStream("d:/c.txt");
                oos = new ObjectOutputStream(fos);
                oos.writeObject(person);
                oos.flush();
                // 反序列化
                fis = new FileInputStream("d:/c.txt");
                // 通过ObjectInputStream将文件中二进制数据反序列化成Person对象:
                ois = new ObjectInputStream(fis);
                Person p = (Person) ois.readObject();
                System.out.println(p);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (oos != null) {
                    try {
                        oos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (ois != null) {
                    try {
                        ois.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    

    注意:

    1. static属性不参与序列化。
    2. 对象中的某些属性如果不想被序列化,不能使用static,而是使用transient修饰。
    3. 为了防止读和写的序列化ID不一致,一般指定一个固定的序列化ID。

 

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注