0
点赞
收藏
分享

微信扫一扫

Java知识三分钟快速上手(去除细枝末叶的细节)


Java快速入门


文章目录

  • ​​Java快速入门​​
  • ​​0 背景​​
  • ​​1 变量​​
  • ​​1.1 primitive类型​​
  • ​​1.2 primitive封装类​​
  • ​​1.3 保留的关键字​​
  • ​​1.4 只有值传递,没有引用传递​​
  • ​​1.5 格式化​​
  • ​​1.4 autoboxing与Unboxing​​
  • ​​2 类和对象​​
  • ​​2.1 实例变量和局部变量​​
  • ​​2.2 堆、栈​​
  • ​​2.2.1 CG​​
  • ​​2.3 构造函数​​
  • ​​2.4 构造执行顺序​​
  • ​​3 继承(extend)​​
  • ​​3.1 原则​​
  • ​​3.2 设计方法​​
  • ​​3.3 意义​​
  • ​​3.4 可以被继承的类​​
  • ​​3.5 覆盖父类的方法​​
  • ​​3.6 不能被实例化的类(抽象类)​​
  • ​​3.7 在父方法中添加内容​​
  • ​​3.8 多态​​
  • ​​3.9 终极对象(object)​​
  • ​​3.10 接口(interface)​​
  • ​​4 静态​​
  • ​​4.1 静态方法​​
  • ​​4.2 静态变量​​
  • ​​4.3 静态final变量​​
  • ​​4.4 静态import​​
  • ​​4.5 静态代码块​​
  • ​​5 异常处理​​
  • ​​5.1 常用格式​​
  • ​​5.2 不处理异常(ducker)​​
  • ​​6 数据存储和使用(序列化)​​
  • ​​6.1 序列化​​
  • ​​6.1.1 序列化声明​​
  • ​​6.1.2 序列化对象写入文件​​
  • ​​6.1.3 解序列化(deserialization) 还原对象​​
  • ​​6.1.4 修改类对序列化的影响​​
  • ​​6.1.5 seriaVersionUID(类的版本id)​​
  • ​​6.2 字符串读写入文件文件​​
  • ​​6.2.1 无缓冲区​​
  • ​​6.2.2 有缓冲区​​
  • ​​其他​​
  • ​​断言​​
  • ​​开启断言​​
  • ​​使用断言​​

0 背景

为了快速复习和捡起java方面的知识,力求短时间内可以看懂java代码。抛去java中细枝末叶的知识点,只学习java的主干知识,于是阅读了《Head First of Java》(很干练,不像《Java核心技术》或《Java编程指南》等书籍,完全可以像字典一样使用),本文就是对此书的总结。如果想熟练的使用Java进行网页的编写,可以结合实际项目,在实际项目中边实践边学习。

1 变量

1.1 primitive类型

​boolean char byte short int long float double​

刚好与下面的句子中的每个字母相对应:

Be Careful! Bears Shouldn’t Ingest Large Furry Dogs

1.2 primitive封装类

Java为这8种基本类型都提供了类的封装,分别为​​Boolean Character Byte Short Integer Long Float Double​

出现的意义(实现万物皆对象):

  • 1)类型之间的转换;
  • 2)泛型中使用;
  • 3)强制类型转换;
  • 4)集合中使用。

基础类型和封装类的区别:

  • 1)传递方式不同
  • 基本类型是按值传递,而封装类型是按引用传递的。int是基本类型,直接存放数值;Integer类会产生一个对象引用指向这个对象。
  1. 存储位置不同
  • 基本类型存储在栈中,封装类的引用存储在栈中,而值是存在堆中。
  1. 比较方式不同
  • 基本型比较使用“==”,封装型比较使用equals();对封装型进行赋值时,不要通过基本型去复制(​​==​​​进行比较的是地址,​​equals()​​​比较的是其中的值)【即,使用​​==​​​来比较两个primitive主数据类型,或者判断两个引用是否引用同一个对象;使用​​equals()​​来判断两个对象是否在意义上相等(如两个String对象是否带有相同字节组合)】。

Java知识三分钟快速上手(去除细枝末叶的细节)_java

  • 4)初始值的不同
  • 封装类型的初始值为null,基本类型的的初始值视具体的类型而定,比如int类型的初始值为0,boolean类型为false;

测试代码(​​参考​​):

public class Test{
public static void main(String[] args){
Integer integer1=new Integer(100);
Integer integer2=new Integer(100);
System.out.println(integer1==integer2);

Integer integer3=100;//实际是Integer。Valueof(1000)
Integer integer4=100;//实际是Integer。Valueof(1000)
System.out.println(integer3==integer4);

Integer integer5=1000;//实际是Integer。Valueof(1000)
Integer integer6=1000;//实际是Integer。Valueof(1000)
System.out.println(integer5==integer6);
}
}


结果解释:



  • false:new在堆中开辟空间 1与2的地址不同,而==仅对地址进行比较,如果使用System.out.println(integer1.equals(integer2));则输出结果为true,因为equals()函数对值进行比较
  • true:100 自动装箱在Integer的缓冲区中,直接返回缓冲区对象,地址相同,为true
  • false:1000已经超出100的缓冲区了,会分配新的地址,所以使用==进行比较时结果false


1.3 保留的关键字

boolean byte char double float int long short public private

protected abstract final native static strictfp synchronized transient volatile if

else do while switch case default for break continue assert

class extends implements import instanceof interface new package super this

catch finally try throw throws return void const goto enum

1.4 只有值传递,没有引用传递

1.5 格式化

Format()格式化参数:​​%[argument number][flags][width][.precision]type​

  • 1)​​%符号​​: 把参数放在这里,若在%后加上,数字将以逗号分隔开,如476,578.10234
  • 2)​​[argument number]​​: 如果要格式化的参数超过一个以上,可以在这里规定是哪一个。
  • 3)​​[flags]​​: 特定类型的特定选项,例如数字里加逗号或正负号
  • 4)​​[width]​​: 宽度,最小的字符数,不足补齐,可超过此宽度
  • 5)​​[.precision]​​: 精确度
  • 6)​​Type​​: 一定要指定的类型标识

例如:

对应

%

[argument number]

[flags]

[width]

[.precision]

type

format("%

1$

,

6

.1

f", 42.2)

例子:

public class Test{
public static void main(String[] args){
int one = 123;
double two = 678.12;
String s= String.format("The rank is %2$,d out of %1$,.2f", two, one);
System.out.println(s);
}
}

使用​​<​​来告诉格式化程序重复利用之前的参数:

Date d = new  Date();
String.format("%tA, %<tB, %<d", d);

1.4 autoboxing与Unboxing

Java 1.5开始出现的特性,可以完成自动的装箱和拆箱(让编译器来自动完成在基本类型和它们的包裹对象之间的转化工作),可以显著提升速度(约5倍)。

未使用​​autoboxing​​的代码:

public class Test{
public static void main(String[] args){
ArrayList lisNumber = new ArrayList();
lisNumber.add(new Integer(3));
Integer one = (Integer) lisNumber.get(0);
int intOne = one.intValue();
System.out.println(intOne);
}
}

等价于下面的代码(​​autoboxing​​):

public class Test{
public static void main(String[] args){
ArrayList<Integer> lisNumber = new ArrayList<Integer>();
lisNumber.add(3);//自动组包
int intOne = lisNumber.get(0);//自动拆包
System.out.println(intOne);
}
}

Auto-Unboxing的局限

Auto-Unboxing的机制则有这样一个局限——只能把包裹类对象往它们对应的基本类型(以及容纳范围更广的类型)上转化。

类似这样的代码是不能工作的,尽管32并未超出byte所能表示的范围(​​参考文章​​):

  • 1,不能同时进行Auto-Unboxing和强制向下转型
    Integer i = new Integer(32);
    System.out.println((byte) i);/* 编译时出错 */
    这是因为编译器并不认可同时进行Auto-Unboxing和强制向下转型的操作,所以这个转化无法进行。如果一定要进行这种操作,需要手工补充一次转型:
  • 2 ,需要先作Unboxing,再强制向下转型
    Integer i = new Integer(32);
    System.out.println((byte)(int) i);
    不过同时进行Auto-Unboxing和强制向上转型的操作是没有问题的,所以下面的代码工作得很正常:
  • 3 ,可以同时进行Auto-Unboxing和强制向上转型
    Integer i = new Integer(32);
    System.out.println((double) i);

2 类和对象

类的权限:​​private​​​,​​default​​​,​​protect​​​,​​public​​(从左到右受限程度越小)。

​private​​​不会被继承,​​public​​会被继承。

对象是类的实例,类是对象的模版。

Java知识三分钟快速上手(去除细枝末叶的细节)_开发语言_02

2.1 实例变量和局部变量

实例变量(声明在类中,而不是方法中):

public class Duck{
int size;
}

局部变量(离开作用域,生命消失):

public void foo(int x ){
int i = x + 2;
boolean b = true;
}

实例变量有默认值,局部变量没有默认值。

2.2 堆、栈

方法和局部变量都是存放在栈,所有对象存放在堆。

栈:

  • 所有局部变量
  • 对象引用变量(指针)与primitive主数据类型变量

堆:

  • 对象本身

2.2.1 CG

一旦对象的引用生命结束,那它所指的对象就会从堆中被踢出。

三种释放对象的方法:

//永久的离开作用域时(被调用方法结束)
void go(){
Life f = new Life();
}

//被引用赋值到其他对象上
Life f = new Life();
z = new Life();

//直接将引用设为空
Life f = new Life();
f = null;

2.3 构造函数

只有当没有手动创建构造函数,编译器才会帮忙创建默无参构造。

Java知识三分钟快速上手(去除细枝末叶的细节)_oop_03

2.4 构造执行顺序

先执行父类的构造,再执行子类的构造。

class Animal{
public Animal(String name){
System.out.println("Making " + name);
}

public void eat(){
System.out.println("Animal");
}
}

class Cat extends Animal{
public Cat(String name){
super(name);
System.out.println("Making Cat");
}

}

public class Test {
public static void main(String[] args){
Cat c = new Cat("miaomiao");
}
}


/*
输出:

Making miaomiao
Making Cat
*/

注意⚠️:​​super​​​也可以换成​​this​​。

3 继承(extend)

Java知识三分钟快速上手(去除细枝末叶的细节)_oop_04

Java知识三分钟快速上手(去除细枝末叶的细节)_java_05

3.1 原则

继承设计原则:当行为程序(程序代码)被多个基本类型共享时,并且二者的关系是​​IS-A​​​的关系(不是​​HAS-A​​的关系),可以考虑继承(子类是父类的特殊类型)。

注意⚠️:不要为了复用其他类的代码,而强行使用继承。例如河马和钢琴都会发声,从而让河马和钢琴都强行继承一个发声类(这完全不合理)。

Java知识三分钟快速上手(去除细枝末叶的细节)_开发语言_06

3.2 设计方法

继承设计的方法:

  • 1,找出具有共同行为和属性的对象;
  • 2,设计代表共同状态和行为的类;
  • 3,决定子类是否需要让某行为(方法的实现)具有特定不同的运行方式;
  • 4,通过寻找使用共同行为的子类来找出更多抽象化的机会(多层抽象);
  • 5,完成类的继承层次。

3.3 意义

继承的意义:

  • 1,避免重复代码(更改代码时,在不破坏子类的情况下,只需要更改父类代码就可以);
  • 2,定义出共同的协议(子类会继承父类的方法,也就是父类可以约束子类【例如抽象方法】)。

3.4 可以被继承的类

  • 1,非公有的类只能被同一个包里的类做子类;
  • 2,使用final修饰的类,表示它是继承的末端,不能被继承;
  • 3,让类只拥有​​private​​​的构造程序(​​constructor​​)。

3.5 覆盖父类的方法

如果想要覆盖父类的方法,就需要自类中覆盖的方法与父类中被覆盖的方法拥有相同的参数、返回类型和更开放的存取权限(方法名也需要相同)。

注意⚠️:区分重载(overload)和覆盖,重载只需要方法名相同,其他都可以不同(但是不能只是返回类型不同,而参数相同),与继承中的覆盖没有关系。

3.6 不能被实例化的类(抽象类)

类前使用修饰符​​abstract​​。

抽象基类只有被继承的作用,抽象的class可以有static成员。

不能在非抽象类中拥有抽象方法。抽象类中可以拥有抽象和非抽象方法。

public abstract class Animal{
public abstract void eat();//没有方法体
}

3.7 在父方法中添加内容

class Animal{
public void eat(){

}
}

class Cat extend Animal{
public void eat(){
super.eat();
//other
}
}

3.8 多态

Animal[] animals = new Animal[3];//可以存放任何Animal的子类
animals[0] = new Dog();
animals[1] = new Cat();
animals[2] = new Tortoise();


for(animal: animals){
animal.eat();//会调用不同动物的eat方法
}
class Vet{
public void gveShot(Animal a){
a.mkaeNoise();
}
}

class PetOwn{
public void start(){
Vet v = new Vet();
Dog d = new Dog();
Cat c = new Cat();
v.giveShot(d);//会执行Dog的mkaeNoise
v.giveShot(c);//会执行Cat的mkaeNoise

}
}

3.9 终极对象(object)

所有类都继承自​​class object​​。

​class oject​​​的一部分方法,​​boolean()​​​、​​equals()​​​、​​Class getClass()​​​、​​int hashCode()​​​、​​String toString()​​​。建议自己写的类覆盖​​int hashCode()​​​、​​String toString()​​​、​​equals()​​这几个方法。

没有继承过其他类的类是隐含的继承对象。

作用:

  • 1,作为多态可以让方法应付多种类型的机制;
  • 2,提供Java执行期对任何对象都有需要的方法的实现的代码(让所有类都会继承到);
  • 3,作为一个通用的、轻量化的对象来实现线程的同步化。

任何从​​ArrayList<Object>​​取出的东西都会被当作Object类型的引用,不管它原来是什么。

编译器会根据引用类来判断哪些method可以调用,而不是根据Object确定的类型。

ArrayList<Oject> aList = new ArrayList<Object>();
Cat c = new Cat();
aList.ddd(c);

Object o = aList.get(0);
o.eat();//错误,Object中没有这样的方法

3.10 接口(interface)

为了解决多重继承的问题(钻石继承),而出现的100%抽象类(类和方法都是​​abstract​​)【这样编译器就知道使用的哪个类中的方法】。

Java知识三分钟快速上手(去除细枝末叶的细节)_面向对象_07

//接口
public interface Pet {
public abstract void beFriendly();
public abstract void play();
}

//基类
class Animal{
public void eat(){
System.out.println("Animal");
}
}


//子类

class Cat extends Animal implements Pet{
@Override
public void play() {

}

@Override
public void beFriendly() {

}

}

4 静态

4.1 静态方法

静态方法:以类的名称调用;非静态方法:以变量的名称调用。

//静态方法
Math.min(22, 89);

//静态方法
Song s = new Song();
s.play();

取得新对象的方法:

  • 1,new;
  • 2,序列化(deserialization);
  • 3,反射(Reflection API)。

静态方法不可以调用类的实例变量和静态方法(无论其中是否包含实例变量)。

public class Test{
int size;
public void doNone(){

}

public static void main(String[] args){
//doNone();//错误:无法从静态上下文中引用非静态 方法 doNone()
System.out.println(size);//错误:无法从静态上下文中引用非静态 变量 size
}

}

4.2 静态变量

概念:它的值对于所有的实例来说都相同,被同类的所有实例共享的变量。

特点:

  • 静态变量会在该类的任何静态方法执行之前完成初始化;
  • 如果静态变量没有被赋予初值,它会被赋予默认初值。

注意⚠️:

  • 实例变量:每个实例一个;
  • 静态变量:每个一个。
public class Test{
int size;
static int count;

public static void main(String[] args){
System.out.println(count);
}
}

4.3 静态final变量

含义:创建时必须被初始化,一旦被初始化后,就不会改动。

public static final double PI_NUM = 3.1415926535

⚠️:

  • 常数变量的名称都应该大写字母,并且以下划线分割。

final扩展:

  • final的变量:不能改变值;
  • final的method:不能覆盖该方法;
  • final的类:不能继承该类(也就是创建它的子类)。

Java自动导入的包,​​java.lang​​。

4.4 静态import

一般导入:

import java.lang.Math;

public class Test{
public static void main(String[] args){
System.out.println(Math.sqrt(22));
}
}

使用static import:

import static java.lang.Math.*;
import static java.lang.System.out;

public class Test{
public static void main(String[] args){
out.println(sqrt(22));
}
}

4.5 静态代码块

静态代码块指 Java 类中的 static{ } 代码块,主要用于初始化类,为类的静态变量赋初始值,提升程序性能。

静态代码块的特点如下:

  • 静态代码块类似于一个方法,但它不可以存在于任何方法体中。
  • 静态代码块可以置于类中的任何地方,类中可以有多个静态初始化块。
  • Java 虚拟机在加载类时执行静态代码块,所以很多时候会将一些只需要进行一次的初始化操作都放在 static 代码块中进行。
  • 如果类中包含多个静态代码块,则 Java 虚拟机将按它们在类中出现的顺序依次执行它们,每个静态代码块只会被执行一次。
  • 静态代码块与静态方法一样,不能直接访问类的实例变量和实例方法,而需要通过类的实例对象来访问。
public class StaticCode {
public static int count = 0;
{
count++;
System.out.println("非静态代码块 count=" + count);
}
static {
count++;
System.out.println("静态代码块1 count=" + count);
}
static {
count++;
System.out.println("静态代码块2 count=" + count);
}

public static void main(String[] args) {
System.out.println("*************** StaticCode1 执行 ***************");
StaticCode sct1 = new StaticCode();
System.out.println("*************** StaticCode2 执行 ***************");
StaticCode sct2 = new StaticCode();
}
}

/*
输出:

静态代码块1 count=1
静态代码块2 count=2
*************** StaticCode1 执行 ***************
非静态代码块 count=3
*************** StaticCode2 执行 ***************
非静态代码块 count=4
*/

Java知识三分钟快速上手(去除细枝末叶的细节)_oop_08

5 异常处理

异常的集成图如下,编译器只检查Exception中的非RuntimeException异常(大部分RuntimeException异常都是程序逻辑错误问题,如空指针、下标越界异常等),如果不处理该类异常,程序就不能编译通过。

Java 程序通常不捕获Error(可以使用​​try/catch块​​​捕获)。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。 Error 用来指示运行时环境发生的错误。. 例如,JVM 内存溢出(​​更多内容​​)。

Java知识三分钟快速上手(去除细枝末叶的细节)_序列化_09

Java知识三分钟快速上手(去除细枝末叶的细节)_开发语言_10

5.1 常用格式

如果如下的格式,处理异常:

try {
//需要捕获的行为
}catch (Exception ex){
//异常发生时执行

}finally{
//都会执行

}


如果要处理多重异常,异常的大小关系需要从小到大(继承树中从下到上),不然可能导致后面需要专门处理的异常种类被前面的代码捕获。

try {
//需要捕获的行为
}catch (){
//异常发生时执行

}catch (){
//异常发生时执行

}catch (){
//异常发生时执行

}


如果有方法可能会抛出异常,就要按如下的方式声明异常(注意区分​​throws​​​和​​throw​​):

public void mothed() throws BadException{
if(condition){
throw new BadException;
}
}

5.2 不处理异常(ducker)

如果不想处理异常,可以使用如下的方法(把异常抛给栈上方的方法):

public class Washer{
Laundry laundry = new Laundry();

public void foo() throw BadException{
laundry.doLaundry;
}

public static void main()throws ClothingException{
Washer a = new Washer();
a.foo();
}
}

Java知识三分钟快速上手(去除细枝末叶的细节)_序列化_11

这里是一个向上抛出,并解决异常的示例:

向上抛出,并解决异常:

public class Test{
public void get() throws Exception{//向上抛出

throw new IndexOutOfBoundsException("越界异常123");

}

public void duck() throws Exception{//向上抛出
get();
}

public void dealException(){//处理异常
try {
duck();
}catch (Exception ex){
System.out.println("越界异常");
}
}

public static void main(String[] args){
Test test = new Test();
test.dealException();
}
}

6 数据存储和使用(序列化)

数据的存储有两种方式:

  • 1,序列化(serialization):只有自己写的java程序会用到这些数据,将序列化的对象写入到文件中;Java知识三分钟快速上手(去除细枝末叶的细节)_面向对象_12
  • 2,纯文本文件:用其他程序可以解析的特殊字符写到文件中。
    Java知识三分钟快速上手(去除细枝末叶的细节)_序列化_13

6.1 序列化

Java知识三分钟快速上手(去除细枝末叶的细节)_序列化_14

6.1.1 序列化声明

实现接口​​Serializable类​​​,如果不想被序列化的变量,可以使用​​transient​​​声明(该值被序列化后,反序列化取值会得到​​null​​)。


⚠️:不把每个类都默认声明序列化的类,是因为这样就无法创建不可被序列化的类;但是我们可以继承不可被序列化的类(没有实现接口​​Serializable类​​​),实现其子类的接口​​Serializable类​​,让其可以被序列化。


示例代码如下:

import java.io.Serializable;

public class Box implements Serializable {
private int bulk;//会被序列化保存的变量
transient String id;//瞬时变量,不能被序列化的变量

public int getBulk() {
return bulk;
}

public void setBulk(int bulk) {
this.bulk = bulk;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}
}

6.1.2 序列化对象写入文件

作用:把对象写入到文件。

流程图:

Java知识三分钟快速上手(去除细枝末叶的细节)_序列化_15


解读:

  • 1,在堆上的对象(在堆上的对象有状态———实例变量的值。这些值让同一类的不同实例有不同的意义);
  • 2,被序列话对象(序列化的对象保存了实例变量的值,因此可以在堆上带回一摸一样的实例)。


示例代码如下:

import com.jk.Box;//前面的实例代码(包名根据自己的情况替换)
import java.io.*;

public class Test{
public static void main(String[] args) throws IOException {
Box box = new Box();
box.setBulk(10);
box.setId("uuid1");
Box box2 = new Box();
box2.setBulk(20);
box2.setId("uuid2");

//字节写入文件(如果文件不存在,会创建文件)
FileOutputStream fileStream = new FileOutputStream("Test.ser");
//把对象转换为写入流的数据
ObjectOutputStream os = new ObjectOutputStream(fileStream);
//写入对象
os.writeObject(box);
os.writeObject(box2);
//关闭所有流串
os.close();
}
}


⚠️:当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个 ​​.ser​​ 扩展名。


6.1.3 解序列化(deserialization) 还原对象

作用:把文件还原为对象。

流程图如下:

Java知识三分钟快速上手(去除细枝末叶的细节)_序列化_16


解读:

  • 1,对象从filestream中读出;
  • 2,Java虚拟机通过存储的信息判断对象的class类型;
  • 3,Java虚拟机尝试和寻找家在对象的类,如果Java 虚拟机找不到或无法加载该类,则Java虚拟机会抛出异常;
  • 4,新的对象被分配到堆上,但构造函数不会被执行;
  • 5,如果对象在继承树上有个不可被序列化的祖先类,则该不可序列化的类以及它之上的类(就算可序列化也一样)的构造函数就会被执行(从第一个不可被序列化的父类开始,全部都会重新初始状态开始);
  • 6,对象的实例变量会被还原成序列化时点的状态值。​​transient变量​​​会被赋值为​​null​​​的对象引用或​​primitive主数据类型​​的默认0、false等值。


示例代码如下:

import com.jk.Box;
import java.io.*;

public class Test{
public static void main(String[] args) throws IOException, ClassNotFoundException {
// Box box = new Box();
// box.setBulk(10);
// box.setId("uuid1");
// Box box2 = new Box();
// box2.setBulk(20);
// box2.setId("uuid2");

//字节读出文件(如果文件不存在,会抛出异常)
FileInputStream fileStream = new FileInputStream("Test.ser");
//把读出流的数据转换为对象
ObjectInputStream os = new ObjectInputStream(fileStream);
//读出对象(读出顺序和写入顺序相同)
Object one = os.readObject();
Object two = os.readObject();
//转换对象类型
Box box = (Box) one;
Box box2 = (Box) two;
//关闭所有流串
os.close();

System.out.println(box.getBulk());
System.out.println(box2.getBulk());
System.out.println(box.getId());
System.out.println(box2.getId());
}
}


⚠️:
1,不把类设计成存储对象的一部分,来以防找不到类的情况,主要原因是这样做非常浪费(尤其是序列化用于网络联机的情况,会造成带宽的消耗)。
2,通过网络传送序列化的对象(可以让类使用URL来定位位置),使用Java的RMI(remove method invocation,RMI,远程调用机制),把序列化对象当参数来进行传递。若调用的Java虚拟机没有这个类的话,可以自动使用URL来取回并加载该类。
3,静态变量不能被序列化(因为是类共有的)。当对象被还原时,静态变量会维持类中原本的样子,而不是存储时的样子。


6.1.4 修改类对序列化的影响

对类的修改,有可能会导致对象反序列化时失败。下面是对类修改,对反序列化的影响的总结:


会损坏反序列化的修改:

  • 1,删除类中的实例变量;
  • 2,改变类中实例变量的类型;
  • 3,将非瞬时的实例变量改为瞬时的;
  • 4,将类从可序列化改为不可序列化;
  • 5,将实例变量改为静态的。

通常不会有事的修改:

  • 1,加入新的实例变量(还原时会使用默认值);
  • 2,在继承层次中加入新的类;
  • 3,在继承层次中删除类;
  • 4,将实例变量从瞬时改为非瞬时(会使用默认值);


6.1.5 seriaVersionUID(类的版本id)

​ seriaVersionUID​​作用:Java序列化是将一个对象编码成一个字节流,反序列化将字节流编码转换成一个对象。 序列化是Java中实现持久化存储的一种方法;为数据传输提供了线路级对象表示法。 Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。

显式​​ seriaVersionUID​​​出现的意义(​​serialVersionUID​​发生变化后,都将导致无法反序化旧有实例,并在反序列化时抛出一个异常):

  • 1,类的​​serialVersionUID​​​的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的​​serialVersionUID​​;
  • 2,当序列化了一个类实例后,不设置serialVersionUID,对类进行修改演进,例如更改一个字段或添加一个字段后,可能会导致​​ seriaVersionUID​​出现变化;
  • 3,如果显示声明后,该类就会一直带着​​serialVersionUID​​​走,不担心​​serialVersionUID​​出现变化。

显示声明​​ seriaVersionUID​​方法(这里使用IDEA自动生成):

设置自动生成​​ seriaVersionUID​

Java知识三分钟快速上手(去除细枝末叶的细节)_开发语言_17

选中对应的类名,然后按 alt+enter 快捷键 ,点击

Java知识三分钟快速上手(去除细枝末叶的细节)_开发语言_18

生成后的结果:

Java知识三分钟快速上手(去除细枝末叶的细节)_开发语言_19

6.2 字符串读写入文件文件

6.2.1 无缓冲区

使用无缓冲区的直接写入的代码,示例如下:

import java.io.*;

public class Test{
public static void main(String[] args) throws IOException {
FileWriter fileWriter = new FileWriter("test.txt");
fileWriter.write("test write");//真正写入
fileWriter.close();
}
}

读出文件的示例如下:

import java.io.*;

public class Test{
public static void main(String[] args) throws IOException {
File file = new File("test.txt");
FileReader fr = new FileReader(file);

int i = 0;
while ((i=fr.read())!= -1) {
System.out.print((char)i);
}

}
}

6.2.2 有缓冲区

带缓冲和不带缓冲的区别在于,带缓冲的​​write​​​方法并不是真正写入数据,而是使用​​flush​​刷新缓存时,才会真正写入数据。

Java知识三分钟快速上手(去除细枝末叶的细节)_oop_20

写缓冲的示例如下:

import java.io.*;

public class Test{
public static void main(String[] args) throws IOException {
FileWriter fileWriter = new FileWriter("test2.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("test1");
bufferedWriter.write("test2");
bufferedWriter.flush();//真正写入
bufferedWriter.close();
}
}

读缓冲的示例如下:

import java.io.*;

public class Test{
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader("test2.txt"));
String str = null;
while((str = bufferedReader.readLine()) != null){
System.out.println(str);//此时str就保存了一行字符串
}
bufferedReader.close();
}
}

其他

断言

作用:使用断言可以免去使用​​System.out.println​​​显示错误信息的麻烦(等程序正确运行后,还要删除这些​​println​​)。

开启断言

开启断言的方法:

  • 1,在命令终端中使用断言(​​java -ea 文件​​​):
    Java知识三分钟快速上手(去除细枝末叶的细节)_开发语言_21
  • 2,在IDEA中使用断言(​​add VM option​​​中添加​​-ea​​​);
    Java知识三分钟快速上手(去除细枝末叶的细节)_面向对象_22
    Java知识三分钟快速上手(去除细枝末叶的细节)_面向对象_23
    Java知识三分钟快速上手(去除细枝末叶的细节)_oop_24

使用断言

//如果条件满足,则执行其他内容;否则,执行表达式,并抛出`Exception`错误
assert (条件): 表达式;
//其他内容
public class Test{
public int func(int n){
assert (n < 0): "num = " + n;
return n * 10;
}

public static void main(String[] args) {
Test t = new Test();
System.out.println(t.func(1));
}

}


⚠️:
断言可以局部开启,如:父类禁止断言,而子类开启断言,所以“断言不具有继承性”。




举报

相关推荐

0 条评论