学习目标:
数据流,对象流,序列化、反序列化,异常,继承,抽象类,反射
学习内容:
1、数据流的使用
数据流: DataInputstream 和 DataOutputStream
DataOutputStream可以帮用户把各种java类型数据转成2进制写入文件流 writeInt(int i) writeLong(long l) writeFloat(float f) writeUTF(String s)
DataInputstream可以帮用户从文件流中直接读取出用户需要的数据 readInt() readLong() readFloat() readUTF()。
练习:
public class DataStreamExcersize {
public static void main(String[] args) throws Exception {
User u1 = new User("张三丰", 100, 10000.0f, 300000000);
User u2 = new User("三毛", 10, 200.0f, 3);
// 将这两个对象的数据写入文件
DataOutputStream dos = new DataOutputStream(new FileOutputStream("d:/u.dat"));
dos.writeUTF(u1.getName());
dos.writeInt(u1.getAge());
dos.writeFloat(u1.getSalary());
dos.writeLong(u1.getHairNum());
dos.writeUTF(u2.getName());
dos.writeInt(u2.getAge());
dos.writeFloat(u2.getSalary());
dos.writeLong(u2.getHairNum());
dos.close();
// 从文件中读出数据,恢复对象
DataInputStream dis = new DataInputStream(new FileInputStream("d:/u.dat"));
String u1_name = dis.readUTF();
int u1_age = dis.readInt();
float u1_salary = dis.readFloat();
long u1_hairNum = dis.readLong();
User user1 = new User(u1_name, u1_age, u1_salary, u1_hairNum);
User user2 = new User(dis.readUTF(), dis.readInt(), dis.readFloat(), dis.readLong());
System.out.println(user1);
System.out.println(user2);
dis.close();
}
}
2、对象流的使用
对象流: ObjectOutputStream 和 ObjectInputStream
ObjectOutputStream可以帮用户直接把一个java的对象变成2进制写入文件流(或者网络流…),必要前提是:该java对象的类型必须实现serializable接口 implements Serializable(可序列化)。
ObjectInputStream可以帮用户从文件流(或者网络流)中读取2进制数据转成一个java对象。
//对象输出流
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/u.obj"));
User user1 = new User("慕容复", 38, 2800, 8000);
User user2 = new User("扫地僧", 58, 3800, 0);
// writeObject(user)方法,要求user对象是可序列化的
oos.writeObject(user1);
oos.writeObject(user2);
oos.close();
}
}
//对象输入流
public class ObjectInputStreamDemo {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/u.obj"));
User readObject1 = (User) ois.readObject();
User readObject2 = (User) ois.readObject();
System.out.println(readObject1);
System.out.println(readObject2);
ois.close();
}
}
3、序列化和反序列化
只是一个概念:
将一个对象变成一个二进制数据序列 --> 这个过程叫序列化
反之,则称为反序列化。
// 复杂类型对象的存、取 ---- 序列化和反序列化
public class FuzaObjectSerDe {
@Test //可直接测试一个方法,不用再写main方法
public void testList() throws Exception {
// 将一个list对象直接写入文件
User user1 = new User("慕容复", 38, 2800, 8000);
User user2 = new User("扫地僧", 58, 3800, 0);
ArrayList<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/users.list"));
oos.writeObject(users);
oos.close();
// 从文件中读取一个list对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/users.list"));
ArrayList<User> userList = (ArrayList<User>) ois.readObject();
System.out.println(userList);
ois.close();
}
//将一个hashmap对象直接写入文件
@Test
public void testWriteMap() throws FileNotFoundException, IOException {
HashMap<String, User> users = new HashMap<>();
User user1 = new User("慕容复", 38, 2800, 8000);
User user2 = new User("扫地僧", 58, 3800, 0);
users.put(user1.getName(), user1);
users.put(user2.getName(), user2);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/users.map"));
oos.writeObject(users);
oos.close();
}
// 直接从文件中读取一个hashmap对象
@Test
public void testReadMap() throws Exception {
// 从文件中读取一个list对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/users.map"));
HashMap<String, User> userMap = (HashMap<String, User>) ois.readObject();
System.out.println(userMap);
}
//写入一个包含list成员的user对象
@Test
public void testWriteUserWithList() throws FileNotFoundException, IOException {
User user1 = new User("慕容复", 38, 2800, 8000);
ArrayList<String> friends = new ArrayList<>();
friends.add("王语嫣");
friends.add("段誉");
friends.add("虚竹");
user1.setFriends(friends);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/murongfu.obj"));
oos.writeObject(user1);
oos.close();
}
}
4、java中的异常Exception处理
Exception是java对程序运行过程中可能出现的不可预料的不正常状态的一种描述。
具体的Exception有各种类型:比如数组脚标越界、list脚标越界、nullpoint异常、NumberFormat异常、ArithmeticException除0异常…
jvm在遇到异常时,会中止程序的执行,并打印出异常信息到控制台。后续代码不会被执行。
捕获异常
那么,我们在写程序时,如果遇到可能出现异常的语句,就可以在这个语句外面包围一个try-catch结构来捕获异常,以便于后续代码可以继续执行。
例:
int a=5;
int b= Integer.parseInt(scanner.nextLine());
try{
int c = a/b;
}catch(Exception e){
System.out.println(e.getMessage());
}
System.out.println("后续代码");
抛异常
抛异常可以直接抛给jvm: 就是说,在main方法中,不对可能出现异常的代码进行try-catch,下层方法可以把异常抛给上层调用者。
比如:定义一个方法,求商,方法声明会抛出异常。
public static int divide(int a,int b) throws Exception{
return a/b;
}
// divide方法的调用者就能获得提示:如何处理异常
// 可以选择继续往上抛
main thows Exception {
divide(5,0);
}
// 也可以选择将异常抓住,不再外抛了
main{
try{
divide(5,0);
}catch(Exception e){
sysout(e.getMessage());
}
}
5、继承
整体作用:对同一类事物进行类定义时,很多的公共属性和方法,可以抽取到一个父类中;具体事物类只要继承这个父类,就拥有了它的属性和方法。
/** 定义一个父类 Person **/
public class Person{
public String name;
public int age;
public float salry;
public void say(){
}
public void eat(){
sysout("正在吃饭....")
}
}
/** 定义一个子类 ChinesePerson **/
public class ChinesePerson extends Person{
// 公共属性和方法就不需要重新定义了(父类中的私有属性和方法不会被继承)
// 可以定义特有属性
public String[] friends;
// 可以定义特有方法
public void makeFriends(String[] friends){
this.friends = friends;
}
// 也可以重写父类中的方法
@Override
public void eat(){
sysout("你好,吃了吗?");
}
}
6、抽象类
抽象类一般用来作为一个父类,里面可以有方法是抽象的,以让子类来实现 。
//abstract 修饰符,表示这个类是一个抽象类
//抽象类和普通类的区别: 抽象类中可以有抽象方法(只有方法定义,没有方法体)
public abstract class AbstractPerson {
public String name;
public int age;
//abstract 声明这个方法是一个抽象方法
public abstract void say();
public void eat() {
System.out.println("正在吃饭......");
}
}
/** 继承了抽象类的子类 **/
public class JapaneseExtendsAbstractPerson extends AbstractPerson{
/**
* 子类中必须实现抽象类中的抽象方法
*/
@Override
public void say() {
System.out.println("雅蠛蝶");
}
}
7、 反射
最直观感受:通过一个字符串类名,就可以通过jdk的反射api来获得这个类的对象instance,还可以通过方法名和参数类型名 来反射出Method。然后可以用method.invok(instance,args)来调用用户所指定的方法。
classname = "com.doit.pojo.Person";
Class<?> forName = Class.forName(classname);
Person o = (Person)forName.newInstance();
反射的基本机制: 可以根据一个“类全名字符串” ,获得代表这个类的class对象,然后根据这个class对象构造出这个类的真正实例对象 。
String className="cn.edu360.javase24.reflect.demo.SerivceOne";
Class<?> forName = Class.forName(className); // 根据类名字符串“SerivceOne”获取class对象
SerivceOne o = (SerivceOne)forName.newInstance(); // 用class对象构造出这个类SerivceOne的实例对象
o.say();String className="cn.edu360.javase24.reflect.demo.SerivceOne";
Class<?> forName = Class.forName(className); // 根据类名字符串“SerivceOne”获取class对象
SerivceOne o = (SerivceOne)forName.newInstance(); // 用class对象构造出这个类SerivceOne的实例对象
o.say();
8、反射举例:
定义两个接口OneTwoService和OtherService,对这两个接口分别写两个实现类。
//OneTwoService接口
public interface OneTwoService {
public void say();
}
//OneTwoServiceOneImpl实现类
public class OneTwoServiceOneImpl implements OneTwoService{
@Override
public void say() {
System.out.println("我是one");
}
}
//OneTwoServiceTwoImpl实现类
public class OneTwoServiceTwoImpl implements OneTwoService{
@Override
public void say() {
System.out.println("我是Two");
}
}
//OtherService接口
public interface OtherService {
public void eat();
}
//OtherServiceOneImpl实现类
public class OtherServiceOneImpl implements OtherService{
@Override
public void eat() {
System.out.println("我是otherserviceoneimpl中的eat方法....");
}
}
//OtherServiceOneImpl实现类
public class OtherServiceTwoImpl implements OtherService{
@Override
public void eat() {
System.out.println("我是otherservicetwoimpl中的eat方法....");
}
}
//测试类
/**
* 这就是一个典型的面向接口编程的程序
*
* 它已经可以称之为一个框架
* @author ThinkPad
*
*/
public class Test {
public static void main(String[] args) throws Exception {
HashMap<String, String> applicationContext = new HashMap<>();
/**
* 解析配置文件,将所有的接口名及其对应的要调用的实现类名放入一个hashmap中
*/
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("f:/a.txt")));
String line = "";
while( (line = br.readLine())!=null ) {
//解析配置文件中的每一行
String[] split = line.split(":");
applicationContext.put(split[0], split[1]);
}
/**
* 先调一下OneTwoService的实现类的say方法
*/
Class<?> forName1 = Class.forName(applicationContext.get("OneTwoService"));
OneTwoService newInstance1 = (OneTwoService) forName1.newInstance();
newInstance1.say();
/**
* 然后需要调OtherService的实现类的eat方法
*/
Class<?> forName2 = Class.forName(applicationContext.get("OtherService"));
OtherService newInstance2 = (OtherService) forName2.newInstance();
newInstance2.eat();
}
}
//反射机制中的方法调用
public class Test{
public static void main(String[] args) throws Exception{
String classname="cn.edu360.java24.day11.reflect.Person";
String methodName="say";
Class<?> forName=Class.forName(classname);
Person p=(Person) forName.newInstance();
//从forName这个class模板中获取到指定的方法
Method method=forName.getMethod(methodName);
Method methos2=forName.getMethod("eat",String.class);
//将method在对象上执行
Object invoke=method.invoke(p);
System.out.println(invoke);
Object invoke=method2.invoke(p,"饺子");
}
}
//若想类名和方法名可随意更换,可以通过main方法的args[]参数来实现。
/**
反射机制的补充:如何使用字符串信息来指定要调用的方法:
字符串指定要调用的方法名;
字符串指定要调用的方法的参数类型;
*/
public class Test{
public static void main(String[] args) throws Exception{
String classname=args[0];//要实例化的类名
String methodName=args[1];//要调用的方法名
Class<?> pClass=Class.forName(args[2]);//方法的参数类型
String food=args[3];//调用方法时要传入的参数值
Class<?> forName=Class.forName(classname);
Person p=(Person) forName.newInstance();
//从forName这个class模板中获取到指定的方法
Method method=forName.getMethod(methodName);
Method methos2=forName.getMethod("eat",String.class);
//将method在对象上执行
Object invoke=method.invoke(p);
System.out.println(invoke);
Object invoke=method2.invoke(p,"饺子");
}
}
args[]的配置方式:
右键选择Run As Configurations,如下图所示,参数间用空格隔开。