一、对象流
1、什么是对象流
对象就是以对象(该对象的类必须实现了序列化接口)作为数据传输的流。
2、序列化
无论是何种类型的数据,都会以二进制序列形式在网络上传送,发送方需要把这个java对象转换为字节序列,才能在网络上传送,同时接收方则需要把字节序列再恢复为对象。
2.1、序列化分为:
序列化和反序列化。
-
序列化:就是将对象的内容分解成字节流,以便存储在文件中或在网络上传输。
-
反序列化:就是打开并读取字节流,且从该流中恢复该对象
2.2、为什么要序列化
如果想在JVM停止后,把这些对象保存到磁盘或者通过网络传输到另一远程机器,磁盘的硬件和网络等是不能认识Java对象,它们只认识二进制这些机器语言,所以我们就要把这些对象转化为字节数组,这个过程就是序列化。
2.3、如何实现序列化
只有实现java.io.Serializable
接口的类才可以启用序列化功能。未实现此接口的类将无法启用任何状态的序列化和反序列化.
-
如果某个类能够被序列化,其子类也可以被序列化。
-
static
修饰的字段是不会被序列化的,序列化保存的是对象的状态而非类的状态,static静态域被忽略 -
transient
修饰符修饰的字段不会被序列化。在序列化某个类的对象时,不希望某个字段被序列化(比如这个字段存放的是隐私值,如:密码等),那这时就可以用transient修饰符来修饰该字段
3、对象流:
4、示例
-
构造一个被序列的类
package day0905; import java.io.Serializable; public class Student implements Serializable { /** * */ private static final long serialVersionUID = 2398123855844975996L; private int id; private String name; private int age; public Student() { // TODO Auto-generated constructor stub } public Student(int id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
-
将对象写在硬盘
package day0905; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class ObjectWriter { public static void main(String[] args) throws IOException { FileOutputStream outputStream = new FileOutputStream("data.txt"); ObjectOutputStream stream = new ObjectOutputStream(outputStream);//构造对象输出流 stream.writeObject(new Student(1001, "tom", 22)); System.out.println("end"); stream.close(); outputStream.close(); } }
-
将对象还原
package day0905; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; public class ObjectReader { public static void main(String[] args) throws IOException, ClassNotFoundException { FileInputStream inputStream = new FileInputStream("data.txt"); ObjectInputStream stream = new ObjectInputStream(inputStream);//构造对象输出流, Student stu = (Student)stream.readObject();//读取对象,并还原 System.out.println(stu); stream.close(); inputStream.close(); } }
二、递归
题目要求不使用循环求出1~100之间的和,该怎么办?
折成两个字来理解:递和归
public class Test{ public static void main(String[] args){ System.out.println(f(100)); } public static int f(n){ if(n==1){ return 1; }else{ return n * f(n-1); } } }
1.1、递归应用场景
-
各种数学问题如:8皇后问题﹐汉诺塔,阶乘问题,迷宫问题,球和篮子的问题(google编程大赛)
-
各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等
-
将用栈解决的问题
1.2、遵守的重要规则:
-
递归一定要有结束条件,且向退出递归的条件靠近,否则就是无限递归,出现StackOverflowError(栈内存溢出)
-
虽然递归有结束条件,但是递归次数太多,也会发生栈内存溢出。
-
构造方法,禁止递归(直接编译报错,因为如果允许无限new对象,会导致堆内存溢出。)
1.3、栈帧(了解)
栈帧(stack frame)是在程序执行过程中,用来存储局部变量、函数参数、返回地址和其他与函数执行相关的信息的一种数据结构。栈帧通常与函数调用有关,每当函数被调用时,就会创建一个新的栈帧。
一个栈帧通常包含以下几个重要的组成部分:
-
局部变量:存储函数中定义的局部变量的值。
-
函数参数:存储函数调用时传递的参数值。
-
返回地址:存储函数执行完后需要返回的下一个指令的地址。
-
调用者保存的寄存器:保存调用者函数在调用当前函数之前需要使用的寄存器的值。
-
栈指针:指向当前栈帧的顶部。
当函数执行结束后,栈帧会被销毁,栈指针会回到上一个栈帧的位置,程序继续执行上一个函数的指令。栈帧的创建和销毁过程遵循先进后出的原则,类似于堆栈(stack)数据结构的操作。
1.4、递归执行原理:
-
执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
-
方法的局部变量是独立的,不会相互影响,比如n变量
-
如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据
-
当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕
-
递归函数在实现的过程中,是一个调用栈的过程。函数在调用另一个函数时,当前函数会处于暂定、未完成的状态,暂停函数的所有变量的值仍然会保存在内存中,直到函数执行完毕,所占内存会被弹出栈
三、反射
反射就是指程序在运行过程中动态地创建对象和调用其内部属性和方法。(常用于各类框架底层实现,了解其基本原理和运行机制,会用即可)
//常用方法 Class<?> c = Class.forName("java.lang.ArrayList"); //获取类本身 Method[] methods = c.getMethods(); //获类中方法列表 Method method = c.getMethod(String name,类<?>... parameterTypes); //获取类中指定方法 method.invoke(Object obj,Object... args); //调用该方法 ArrayList<String> arr = c.newInstance(); //根据类,new对象