Java基础4

一、java  StringBuffer和StringBuilder类

对字符串进行修改的时候,需要使用StringBuffer和StringBuilder类。StringBuilder类比StringBuffer类要快,但是StringBuilder不是线程安全的(即不能同步访问)。在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

    public static void main(String args[]) {
        StringBuffer sbuff=new StringBuffer("修改这个字符串:");
        sbuff.append("添加字符串");  //添加字符串
        System.out.println(sbuff);
        sbuff.reverse();  //翻转字符串
        System.out.println(sbuff);
        sbuff.delete(2,4);//移除第二个到第四个,但不包括第四个(下标从0开始)
        System.out.println(sbuff);
        sbuff.insert(1,"aaa");//offset在指定位置插入元素,str可以是数字、字符、字符串
        System.out.println(sbuff);
        sbuff.replace(2,5,"替换"); //指定开始和结束位置,替换。从第二个到第五个,不包括第五个
        System.out.println(sbuff);
    }

输出结果:

修改这个字符串:添加字符串
串符字加添:串符字个这改修
串符添:串符字个这改修
串aaa符添:串符字个这改修
串a替换添:串符字个这改修

二、java----基本数据类型和引用数据类型作为参数的区别

java中的方法可以传递参数,参数的传递方法就是值传递。

参数有形参和实参,定义方法时写的参数叫形参,真正调用方法时,传递的参数叫实参。

调用方法时,会把实参传递给形参,方法内部其实是在使用形参。

所谓值传递就是当参数是基本类型时,传递参数的值,比如传递i=10,真实传参时,把10赋值给了形参。

当参数是对象时,传递的是对象的值,也就是对象的首地址。就是把对象的地址赋值给形参。

基本类型当做方法的形参时的传参过程

引用类型当做方法的形参时

基本数据类型和引用数据类型作为参数的区别
基本数据类型的变量中直接存放数据值本身,所以改的时候改的是数据值本身;
但是引用类型不同的地方在于真正的数据并没有在栈区的变量中保存,而是在堆区里面保存着,所以虽然也拷贝了一份,也是副本,但是二者指向的是同一块堆区。

引用数据类型就好比如说,两位同学使用的是同一份复习资料,其中一人把资料撕毁了,另一人当然也会受到影响。
而基本数据类型就好比复印了一份,其中一人将自己的资料撕了,并不影响别人。

总结:
1).当使用基本数据类型作为方法的形参时,在方法体中对形参的修改不会影响到实参的数值
2).当使用引用数据类型作为方法的形参时,若在方法体中修改形参指向的数据内容,则会
* 对实参变量的数值产生影响,因为形参变量和实参变量共享同一块堆区;*
3).当使用引用数据类型作为方法的形参时,若在方法体中修改形参变量的指向,此时不会
* 对实参变量的数值产生影响,因此形参变量和实参变量分别指向不同的堆区;*

1、将数组中的元素颠倒位置,在一个单独的方法里想实现这个功能的话,不能传递值,而应该传递数组引用。代码如下

public class Main {
    int[] aths=new int[]{1,2,3,4};
    public void vari(int[] args,int i,int j){
        int temp=args[i];
        args[i]=args[j];
        args[j]=temp;
    }
    public void pei(){
        vari(aths,0,1);//传递数组的引用,加上索引来调用数组元素,这样才能改变数组的值
    }
    public static void main(String args[]) {
        Main mm=new Main();
        mm.pei();
        System.out.println(Arrays.toString(mm.aths));
    }
}

 2、io输出流,println方法对于char类型的数组来说,它定义的方法是遍历这个数组,输出数组的内容。对于int类型来说,aths里存储的是int类型数组的首地址,输出的是地址

public class Main {
    public static void main(String args[]) {
        int[] aths=new int[]{1,2,3,4};
        char[] biu=new char[]{'a','b','c'};
        System.out.println(aths);//输出[I@4a574795
        System.out.println(biu);//输出abc
    }
}

3、实现method方法,使输出a=100,b=200。因为method中的参数是基本数据类型,不会被修改。

public class Main {
    public static void main(String args[]) {
        int a=10;
        int b=20;
        method(a,b);
        System.out.println("a="+a);
        System.out.println("b="+b);
    }
    public static void method(int a,int b){
        a=a*10;
        b=b*10;
        System.out.println("a="+a);
        System.out.println("b="+b);
        System.exit(0);//System.exit(int status)表示结束当前正在运行的java虚拟机,System.exit(0);表示正常退出。
        // System.exit(1);通常放在catch块中,表示非正常退出
    }
}

 

对象数组的内存解析

三、java日期时间

java提供Date类封装当前的日期和时间

1、获取当前的日期时间   toString()

 public static void main(String args[]) {
        Date date=new Date();
        System.out.println(date.toString());
    }

2、格式化日期   SimpleDateFormat()

public static void main(String args[]) {
        Date date=new Date();
        SimpleDateFormat ft=new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss");
        System.out.println(ft.format(date)); //2020-11-14  11:31:23
    }

注意:

 yyyy 是完整的公元年,MM 是月份,dd 是日期,HH:mm:ss 是时、分、秒。

有的格式大写,有的格式小写,例如 MM 是月份,mm 是分;HH 是 24 小时制,而 hh 是 12 小时制。

四、方法

1、方法的重载

方法签名=方法名+参数列表

方法重载指的是一个类中有多个名字相同的方法,但不同的方法有不同的参数列表。java编译器通过根据方法签名来判断哪个方法应该被调用

2、可变参数

适用于参数个数不确定,参数类型确定的情况。java把可变参数当做数组处理。

可变参数必须位于参数列表的最后,任何普通的参数必须在可变参数之前声明。

一个方法中只能指定一个可变参数。

public class Main {
    public int vari(int i,int...args){
        int sum=i;
        for(int j=0;j<args.length;j++){
            sum+=args[i];
        }
        return sum;
    }
    public static void main(String args[]) {
        Main mm=new Main();
        int gg=mm.vari(1, 1,2,3,45,5);
        System.out.println(gg);
    }
}

五、java异常

1、error---错误 : 是指程序无法处理的错误,表示应用程序运行时出现的重大错误。例如jvm运行时出现的OutOfMemoryError以及Socket编程时出现的端口占用等程序无法处理的错误。

2、Exception --- 异常 :异常可分为运行时异常跟编译异常

编译异常:RuntimeException以外的异常。这类异常在编译时编译器会提示需要捕获,如果不进行捕获则编译错误。常见编译异常有:IOException(流传输异常),SQLException(数据库操作异常)等。

运行时异常:即RuntimeException及其之类的异常。这类异常在代码编写的时候不会被编译器所检测出来,是可以不需要被捕获,但是程序员也可以根据需要进行捕获抛出。常见的RUNtimeException有:NullpointException(空指针异常),ClassCastException(类型转换异常),IndexOutOfBoundsException(数组越界异常)等。

所有的异常类是从 java.lang.Exception 类继承的子类。

Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。异常类有两个主要的子类:IOException 类和 RuntimeException 类

3、java处理异常的机制:抛出异常以及捕获异常 ,一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕捉的。

4、throw跟throws的区别:

public void test() throws Exception {
    throw new Exception();
}

throws表示一个方法声明可能抛出一个异常,throw表示此处抛出一个已定义的异常(可以是自定义需继承Exception,也可以是java自己给出的异常类)。 

5、接下来看一下如何捕获异常:

1)首先java对于异常捕获使用的是try---catch或try --- catch --- finally 代码块,程序会捕获try代码块里面的代码,若捕获到异常则进行catch代码块处理。若有finally则在catch处理后执行finally里面的代码。然而存在这样两个问题:

a.看如下代码:

try{
    //待捕获代码
}catch(Exception e){
    System.out.println("catch is begin");
    return 1 ;
}finally{
     System.out.println("finally is begin");
}

在catch里面有一个return,那么finally会不会被执行呢?答案是肯定的,上面代码的执行结果为:

catch is begin
finally is begin  

也就是说会先执行catch里面的代码后执行finally里面的代码最后才return1 ;

b.看如下代码:

try{
   //待捕获代码    
}catch(Exception e){
    System.out.println("catch is begin");
    return 1 ;
}finally{
     System.out.println("finally is begin");
     return 2 ;
}

在b代码中输出结果跟a是一样的,然而返回的是return 2 ; 原因很明显,就是执行了finally后已经return了,所以catch里面的return不会被执行到。也就是说finally永远都会在catch的return前被执行。(这个是面试经常问到的问题哦!)

6、对于异常的捕获不应该觉得方便而将几个异常合成一个Exception进行捕获,比如有IO的异常跟SQL的异常,这样完全不同的两个异常应该分开处理!而且在catch里处理异常的时候不要简单的e.printStackTrace(),而是应该进行详细的处理。比如进行console打印详情或者进行日志记录。

注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。

异常方法:

public String getMessage()   返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。

public Throwable getCause() 返回一个Throwable 对象代表异常原因。

public String toString()  使用getMessage()的结果返回类的串级名字。

public void printStackTrace()   打印toString()结果和栈层次到System.err,即错误输出流。

多重try  catch块的时候应该把一些小的可预见的异常放在前面,最后才放这些小异常的父类,否则这些小异常都会被大异常捕获,就不会继续往下走catch块了

声明自定义异常

在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。

  • 所有异常都必须是 Throwable 的子类。
  • 如果希望写一个检查性异常类,则需要继承 Exception 类。
  • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

六、获取控制台输入

1、Scanner类

用于从控制台接受用户输入

Scanner sc =new Scanner(System.in)

获取输入的字符串有两种方式next()和nextLine()。在读取前我们一般需要 使用 hasNext 与 hasNextLine 判断是否还有输入的数据:

对于next(),

一定要读取到有效字符后才可以结束输入

对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。

next()将空格和回车都视为结束

nextLine()

以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。可以获得空白

2、BufferedReader类

public class Sctes {
    public static void main(String[] args) throws IOException {//io流操作必须抛出io异常
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        int a=br.read(); //无论控制台输入多少字符或字符串,都只接受一个字符。用来读取一个字符,单独的回车、空格也可以被当做一个字符,输出的是一个字符的asc码,是int类型。
        String c=br.readLine();//只能执行一次,可以包含空格,以回车键作为结束,是String类型.输出的是字符串
        System.out.println(a);
        System.out.println(c);
    }
}

七、IO流

流的分类:按操作数据单位不同分为字节流(8bit)【图片、视频】和字符流(16bit)【文本文件】。按流入的对象分为输入流和输出流。输入流和输出流都是针对程序而言的。处理流是对节点流(文件流)做进一步处理

对于文本文件(.txt,.c,.java),使用字符流处理

对于非文本文件(图片,视频,音频,doc,ppt),使用字节流处理

File f=new File("文件路径")----这个文件路径可以是相对路径可以是绝对路径。如果这个语句写在main方法中,那么这个相对路径是相对于项目的路径而言的,写在main方法中需要特别注意,路径可能会和方法中的不同,需要补充路径位置。如果是写在方法中,那么这个相对路径是相对于这个方法而言的。

如上图所示,在src下新建一个hello.txt(注意:在创建的时候自己补上后缀名,通过idea创建的txt文件可能会识别不了)

在main方法中想要用相对路径调用这个文件,用的路径如上所示。项目的路径是E:\new     所以写的是相对于E:\new的,路径前不加\\

访问文件字符输入流FileReader

法一:

public int read() throws IOException
读取单个字符,返回一个int型变量代表读取到的字符

public class Sctes {
    public static void main(String[] args)  {//io流操作必须抛出io异常
        //创建一个文件代理,不是创建文件,指向要操作的文件
        //针对文件的抛异常最好不要用throws,因为throws抛出异常后,就不会再往下执行了。但是try  catch抛出异常之后还是会继续向下运行,因为还需要向下运行关闭流的操作
        FileReader fr=null;
        try{
            //提供具体的流
            //如果要读的文件不存在,就会抛出异常。fr这个引用仍然为空,且没有开启文件流。因此也就没必要执行finally中的关闭流操作
            fr=new FileReader("src\\hello.txt");//在try  catch里定义的变量是局部变量,如果在try  catch块外面还想调用块里面的变量,需要在块外提前声明。
            //数据的读入,read()返回读入的一个字符,如果达到文件末尾,返回-1
            int data=fr.read();
            while(data!=-1){
                System.out.print((char)data);
                data=fr.read();//能自动读取下一个字符,不用自己给往下挪位置
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally{//finally中放一定必须执行的东西,因为finally中的语句必执行。流必须要执行关闭操作
            try{
                //流的关闭操作
                //垃圾回收机制只回收JVM堆内存里的对象空间,对其他物理连接,比如数据库连接、输入输出流、socket链接无能为力,必须手动关闭。否则容易导致内存泄漏
                if(fr!=null)//如果没有开启文件流,fr仍然为空,此时不需要关闭文件流
                    fr.close(); //关闭流的操作也有可能会出现异常
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}

法二:

public int read(char [] c, int offset, int len)
读取字符到c数组,返回读取到字符的个数

public class Sctes {
    public static void main(String[] args)  {//io流操作必须抛出io异常
        FileReader fr=null;
        try{
            //1.File类的实例化
            File file=new File("src\\hello.txt");
            //2.FileReader流的实例化
            fr=new FileReader(file);
            //3.读入操作
            //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数,如果达到文件末尾,则返回-1
            int len;
            char[] c=new char[5];
            //针对文件中的字符个数大于定义的数组的长度
            while((len= fr.read(c))!=-1){//之所以要while循环是因为数组长度小于文件中字符长度,不需要自己写自增,会自动先读取前五个,之后移动到第六个位置,再读取之后的五个
                //错误写法。定义的数组的大小是5,而文件中存储的字符的个数是13个,需要将这13个分成3批分别存放到数组c中
                //c.lenth=5,对于第二组来说,c数组里面存放的是world,在存放第三组的时候如果判断的依据是i<c.length,则c数组中前三个
                //存放的是hio,后两个还是ld没变,所以输出结果会有问题
//                for(int i=0;i<c.length;i++)
//                    System.out.print(c[i]);//输出结果  helloworldhiold
                //正确写法
                //此处len中存放的是read读取到的字符个数
                for(int i=0;i<len;i++)
                    System.out.print(c[i]);//输出结果  helloworldhio
            }
            fr.read(c);
        }catch (IOException e){
            e.printStackTrace();
        }finally{
            try{
                if(fr!=null)
                    fr.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

访问文件字符输出流FileWriter

public class Sctes {
    public static void main(String[] args){
        //1、提供File类的对象,指明写出到的文件
        File h=new File("hello1.txt");
        //2、提供FileWriter的对象,用于数据的写出
        //FileWriter fw = new FileWriter(h,false);  或者 FileWriter fw = new FileWriter(h);时,直接覆盖原有文件
        //FileWriter fw = new FileWriter(h,true);   在原有文件的末尾处追加
        FileWriter fw = null;
        try {
            fw = new FileWriter(h,true);
            //3、写出数据
            fw.write("i have a dream");
            fw.write("you should dooo");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fw!=null) {
                try {
                    //4、关闭流资源
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

使用FileReader和FileWriter方法实现文件的复制

public class Sctes {
    public static void main(String[] args){
        //1、提供File类的对象,指明读的文件
        //                 ,指明写的文件
        FileReader fr=null;
        FileWriter fw=null;
        try{
            File hh=new File("src\\hello.txt");
            File h=new File("hello1.txt");
            //2、提供FileReader的对象,用于数据的读入
            //  提供FileWriter的对象,用于数据的写出
            fr = new FileReader(hh);
            fw = new FileWriter(h);
            char[] c=new char[5];
            int len;
            //public void write(char [] c, int offset, int len)
            //写入字符数组中开始为offset长度为len的某一部分
            while((len=fr.read(c))!=-1){
                fw.write(c,0,len);
            }
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

访问文件字节输入流FileInputStream访问字节文件输出流FileOuputStream.与字符的类似,只不过存储的数组由字符型变为了字节型

//图片的复制
public class Sctes {
    public static void main(String[] args){
        FileInputStream fs=null;
        FileOutputStream fo=null;
        try{
            File fi=new File("newnnnn.png");
            File f2=new File("newpppp.png");

            fs=new FileInputStream(fi);
            fo=new FileOutputStream(f2);

            byte[] bytt=new byte[5];
            int len;
            while((len=fs.read(bytt))!=-1){
                fo.write(bytt,0,len);
            }
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(fs!=null) {
                try {
                    fs.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fo!=null) {
                try {
                    fo.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

缓冲流(字节型)实现非文本文件的传输

缓冲流是处理流,是作用在字节流上的。因此要先定义字节流后再定义缓冲流。使用数组进行传输的思想是一致的。缓冲流的速度要比普通的字节流、字符流快。作用是提高的读取速度,原理是内部提供了一个缓冲区,缓冲区满了之后才会进行一次传输

public void Bufferin(String a,String b){
        //创建文件引用
        BufferedInputStream bi=null;
        BufferedOutputStream bo=null;
        try{
            File f1=new File("newnnnn.png");
            File f2=new File("newgggg.png");
            //创建字节流
            FileInputStream fi=new FileInputStream(f1);
            FileOutputStream fo=new FileOutputStream(f2);
            //创建缓冲流
            bi=new BufferedInputStream(fi);
            bo=new BufferedOutputStream(fo);
            //读取数据
            byte[] by=new byte[1024];
            int len;
            while((len=bi.read(by))!=-1)
                bo.write(by,0,len);
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(bi!=null) {
                try {
                    bi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bo!=null) {
                try {
                    //关闭数据流。先关外部,后关内部。关外部的时候内部的会自动关闭,只用关外部的就行
                    bo.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

缓冲流(字符型)实现文本文件的传输

public void Bufferin(String a,String b){
        BufferedReader br=null;
        BufferedWriter bw=null;
        try{
            //可以使用匿名对象来创建
            br=new BufferedReader(new FileReader(new File("Hello1.txt")));
            bw=new BufferedWriter(new FileWriter(new File("Hello2.txt")));
            String path=null;
            //BufferedReader还有一个除了字符数组和单个字符读取之外,还可以一行一行的读取。如果读取到末尾,返回null
            while((path=br.readLine())!=null)
                bw.write(path);
        }catch(IOException e){
            e.printStackTrace();
        }finally {
            if(br!=null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bw!=null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

转换流。InputStreamReader 将字节流转化为字符流。  OuputStreamWriter将字符流转化为字节流

public class Sctes {
    public static void main(String[] args){
        InputStreamReader is=null;
        try{
            //以字节流的形式读入文件内容
            FileInputStream  fi=new FileInputStream("Hello1.txt");
            //使用默认的字符集转换
//            is=new InputStreamReader(fi);
            //指定转化的字符集,选择字符集依赖于被转化的文件之前字符集格式
            is=new InputStreamReader(fi,"UTF-8");
            char[] s=new char[20];
            int len;
            while((len=is.read(s))!=-1){
                String str=new String(s,0,len);
                System.out.println(str);
            }
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(is!=null) {
                try {
                    is.close();//快捷键Alt+Enter直接弹出try catch
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

转换流实现文件的读入和写出

public class Sctes {
    public static void main(String[] args){
        InputStreamReader is=null;
        OutputStreamWriter os=null;
        try{
            //以字节流的形式读入文件内容
            FileInputStream  fi=new FileInputStream("Hello1.txt");
            //以字节流的形式写入文件内容
            FileOutputStream fp=new FileOutputStream("Hello3.txt");
            //使用默认的字符集转换
//            is=new InputStreamReader(fi);
            //指定转化的字符集,选择字符集依赖于被转化的文件之前字符集格式
            is=new InputStreamReader(fi,"UTF-8");
            os=new OutputStreamWriter(fp,"gbk");
            char[] s=new char[20];
            int len;
            while((len=is.read(s))!=-1){
                os.write(s,0,len);//write方法用来实现将字符串转化为字节
            }
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(is!=null) {
                try {
                    is.close();//快捷键Alt+Enter直接弹出try catch
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(os!=null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

八、序列化和反序列化(ObjectOutputStream和ObjectInputStream)

在分布式环境下,当进行远程通信时,无论是何种类型的数据,都会以二进制序列的形式在网络上传送。序列化是一种将对象以一连串的字节描述的过程,用于解决在对对象流进行读写操作时引发的问题。序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统里,并在需要的时候把流读取出来重新构造一个相同的对象。

注意:所有要实现序列化的类都必须实现Serializable接口

序列化的特点:

(1)如果一个类能被序列化,那么它的子类也能够被序列化

(2)由于static(静态)代表类的成员,transient(java语言关键字,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持)代表对象的临时数据,因此被声明为这两种类型的数据成员是不能被序列化

实例:

①:首先定义一个类Seriliza

Seriliza.java

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Seriliza implements Serializable{  //必须继承Serializable接口
    private static final long serialVersionUID = 1L;//必须要有的
    transient int ssn=3;  //临时数据,它的值不会维持,因此它最终的值是它所属类型的默认值
    static int kou=4;   //静态类属性,不会序列化
    String name="moumou";
    int year=1997;
    float num=4.0f;
    public void intName(){
        System.out.println("hhh");
    }
}

 注意:在序列化与反序列化的过程中,serialVerisonUID起着非常重要的作用,每个类都一个特定的serialVerisonUID,在反序列化的过程中,通过serialVerisonUID来判定类的兼容性,如果待序列化的对象与目标对象的serialVerisonUID不同,那么在反序列化时就会抛出InvalidClassException异常。

自定义显示声明serialVerisonUID的优点

               》提高程序的运行效率,如果在类中未显示声明,那么在序列化时会自动计算得到一个serialVerisonUID值。通过显示声明的方式省去了计算的过程,能提高程序的运行效率。

              》提高程序不同平台上的兼容性。由于各个平台的编译器在计算serialVerisonUID时完全可能会采用不同的计算方式,这就会导致在一个平台上序列化的对象在另外一个平台上将无法实现反                   序列化的操作。通过显示声明serialVerisonUID可以完全避免该问题

             》增强程序各个版本的可兼容性。在默认情况下,每个类都有唯一的serialVerisonUID,因此,当后期对类进行修改时(例如加入新的属性),类的serialVerisonUID值将会发生变化,这将会                  导致类在修改前对象序列化的文件在修改后将无法进行反序列化操作。通过显示声明serialVerisonUID可以完全避免该问题

②:定义一个Seriliza的子类Seriason

Seriason.java

public class Seriason extends Seriliza{
}

③:序列化

Serilizab.java

序列化是将对象写成序列,这里要将序列化后的流写入文本文件中,因此先用FileOutputStream(对于程序而言是向外输出流的),然后再用ObjectOutputStream来对文件流做处理,用到的方法是writeObject。这个方法需要抛出IOException。try和catch中定义的变量都是局部变量,也就是说try中定义的变量catch是没法用的,如果想try和catch都操作同一个变量,需要在外部定义这个变量。

import javax.imageio.IIOException;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Serilizab {
    public static void main(String[] args) {
        Seriason sr = new Seriason();
        sr.ssn=4;
        sr.kou=7;
        sr.name="niuniu";
        sr.num=7.8f;
        sr.year=2000;
        try {
            FileOutputStream fp = new FileOutputStream("D:\\aa.txt");
            ObjectOutputStream op = new ObjectOutputStream(fp);
            op.writeObject(sr);
            op.close();
            fp.close();
        } catch (IOException f) {
            f.printStackTrace();

        }
    }
}

④:反序列化

SerilizaW.java

反序列化是将字节流转成对象,因为是将文件中的流读入到程序中,所以先用FileInputStream,然后再用ObjectInputStream。用到的方法是readObject,这个方法返回的是object类型,我们根据需要将这个object转换为我们的类的类型。在读的时候,jvm必须要找到字节码的类,因此必须要抛出ClassNotFoundException,还要抛出IOException。

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class SerilizaW {
    public static void main(String[] args) {
        Seriason sl=null;//s1不能定义在try里,因为try里是局部变量
        try{
            FileInputStream fi=new FileInputStream("D:\\aa.txt");
            ObjectInputStream oi=new ObjectInputStream(fi);
            sl=(Seriason) oi.readObject();//将读取到的Object类型转化为Seriason
        }catch(IOException e){
            e.printStackTrace();
        }catch(ClassNotFoundException c){
            c.printStackTrace();
        }
        System.out.println(sl.kou);//kou是静态变量,不会被序列化,因此也不会因为在Serilizab中修改属性值而改变,它的值依然是类中给定义的初始值
        System.out.println(sl.ssn);//ssn是transient临时变量,里面的值不会一直维持,结果是他所属类型的默认值
        System.out.println(sl.name);
        System.out.println(sl.num);
    }
}

输出结果:

4
0
niuniu
7.8

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值