Java基础二---Java中的50个关键字

访问修饰符的关键字(3个)---封装

public

protected

private

定义类、接口、抽象类和实现接口、继承类的关键字、实例化对象(6个)---继承、多态

class:类

interface:接口

abstract:声明抽象

implement:实现

extends:继承

new :创建新对象

抽象类和接口

接口

接口在Java语言中是一个抽象类型,是服务提供者和服务使用者之间的一个协议,在JDK1.8之前一直是抽象方法的集合,一个类通过实现接口从而来实现两者间的协议。接口可定义字段和方法。

在JDK1.8之前,接口中所有的方法都是抽象的,从JDK1.8开始,也可以在接口中编写默认的和静态的方法。除非显式指定,否则接口方法都是抽象(abstract)的。(这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。)

接口的特点:

  • 接口没有构造方法

  • 接口不能用于实例化对象

  • 接口中的字段必须初始化,并且隐式地设置为公有的(public)、静态的(static)和final的。因此,为了符合规范,接口中的字段名全部要大写

  • 接口不是被类继承,而是要被类实现(implement)

  • 接口中每一个方法默认是公有和抽象的,即接口中的方法会被隐式的指定为public abstract。从JDK1.8开始,可以在接口中编写默认的和静态的方法。声明默认方法需要使用关键字default

  • 当类实现接口时,类要实现接口中的所有的方法。否则,类必须声明为抽象的。

  • 接口支持多重继承,即可以继承多个接口

抽象类

在面向对象的概念中,所有的对象都是通过类来描绘的。但并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类(Abstract)抽象类除了不能实例化对象之外,类的其他功能依然存在,成员变量,成员方法和构造方法的访问方式和普通类一样。

由于抽象类不能实例化对象,所以抽象类必须被继承才能被使用。(抽象类和普通类最大的区别)

如果想要设计这一一个类,该类包含一个特别的成员方法,方法的具体实现由它的子类确定,那么可以在父类中声明该方法为抽象方法Abstract关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体声明抽象方法会造成以下两个结果:

  • 如果一个类包含抽象方法,则该类必须声明为抽象类
  • 子类必须重写父类的抽象方法,否则自身也必须声明为抽象类

接口和抽象类的区别

  1. 接口中的方法没有方法体,但抽象类可以实现方法的具体功能

  2. 抽象类中的成员可以是各种类型的,接口中的成员变量只能是public static final类型的

  3. 一个类只能继承一个抽象类,但能实现多个接口

  4. 抽象类可以有构造体,接口不能有构造体

  5. 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。

同时抽象类和接口都不能使用final修饰

包的关键字(2个)

import

package

数据类型的关键字(8个)

  • byte
    字节长度的整数,八位
  • char
    Unicode字符,十六位
  • short
    短整数,十六位
  • int
    整数,三十二位
  • long
    长整数,六十四位
  • float
    单精度浮点数,三十二位
  • double
    双精度浮点数,六十四位
  • boolean
    布尔值

String

String对象是如何实现的

  • Java6:char[]、offset、count、hash
  • Java7/8:char[]、hash
  • Java9:byte[]、coder、hash
  1. 在Java6以及之前的版本中,String对象是对char数组进行了封装实现的对象,主要有四个成员变量:char数组、偏移量offset、字符数量count、哈希值hash
    String对象是通过offset和count两个属性来定位char[]数组,获取字符串。这么做可以高效、快速地共享数组对象,同时节省内存空间,但这种方式很有可能会导致内存泄漏

  2. 从Java7版本开始到Java8版本,Java对String类做了一些改变。String类中不再有offset和count两个变量了。这样的好处是String对象占用的内存稍微少了些,同时,String.substring方法也不再共享char[],从而解决了使用该方法可能导致的内存泄漏

  3. 从Java9版本开始,工程师将char[]字段改为了byte[]字段,又维护了一个新的属性coder,它是一个编码格式的标识
    一个char字符占16位,2个字节。这个情况下,存储单字节编码内的字符(占一个字节的字符)就显得非常浪费。JDK1.9的String类为了节约内存空间,于是使用了占8位,1个字节的byte数组来存放字符串
    而新属性coder的作用是,在计算字符串长度或者使用indexOf()函数时,我们需要根据这个字段,判断如何计算字符串长度。coder属性默认有0和1两个值,0代表Latin-1(单字节编码),1代表UTF-16。如果String判断字符串只包含了Latin-1,则coder属性值为0,反之则为1

创建String的方式有:
将一个字符串字面值赋值给一个String引用变量 String s="Java";
或者用new关键字来构建一个String对象 String s=new String("Java");
这两种创建方式的含义并不相同
当代码中使用第一种方式创建字符串对象时,JVM首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将在常量池中被创建。
这种方式可以减少同一个值的字符串对象的重复创建,节约内存。
String str = new String(“Java”) 这种方式,首先在编译类文件时,"Java"常量字符串将会放入到常量结构中,在类加载时,“Java"将会在常量池中创建;其次,在调用 new 时,JVM 命令将会调用 String 的构造函数,同时引用常量池中的"Java” 字符串,在堆内存中创建一个 String 对象;最后,str 将引用 String 对象。

因此,使用字符串字面值会更好,因为JVM节省了一些本来需要用来构造新的实例的CPU周期。
例:

new Integer(123) 每次都会新建一个对象
Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。

String.intern()

如果常量池中有相同值,就会重复使用该对象,返回对象引用,这样一开始的对象就可以被回收掉

在字符串常量中,默认会将对象放入常量池;在字符串变量中,对象是会创建在堆内存中,同时也会在常量池中创建一个字符串对象,String对象中的char数组将会引用常量池中的char数组,并返回堆内存对象引用。
如果调用intern方法,会去查看字符串常量池中是否有等于该对象的字符串的引用,如果没有,在JDK1.6版本中会复制堆中的字符串到常量池中,并返回该字符串引用,堆内存中原有的字符串由于没有引用指向它,将会通过垃圾回收器回收。
在 JDK1.7 版本以后,由于常量池已经合并到了堆中,所以不会再复制具体字符串了,只是会把首次遇到的字符串的引用添加到常量池中;如果有,就返回常量池中的字符串引用。

String不可变的好处

  1. 可以缓存 hash 值
    因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

  2. String Pool 的需要
    如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。

  3. 安全性
    String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。

  4. 线程安全
    String 不可变性天生具备线程安全,可以在多个线程中安全地使用。

StringBuffer和StringBuilder

String对象是不可变的,如果需要多次向其添加或者插入字符时,使用它并不合适,因为每次都会创建一个新的String对象 此时最好使用StringBuffer或者StringBuilder,在完成操作后再将其转为String对象

StringBuffer的方法是同步的(内部使用 synchronized 进行同步),这使得StringBuffer适合在多线程环境下使用,但同步的代价是性能的下降。而StringBuilder是StringBuffer的异步版本,如果不需要同步的话,应优先选择StringBuilder

如果在创建StringBuilder对象时没有指定大小,这个对象将拥有十六个字符的初始大小。如果内容超过了其容量,对象将自动增加大小。如果在创建前知道内容将会超过十六个字符,最好分配足够的空间,因为增加容量也会花费一定时间


包装类型和基本类型

包装类型和基本类型的区别:

1.包装类型可以为null,而基本类型不可以

2.包装类型可用于泛型,而基本类型不可以

3.基本类型比包装类型更高效

4.两个包装类型的值可以相同,但却不相等

5.自动装箱和自动拆箱
当需要进行自动装箱时,如果数字在-128-127之间,会直接使用缓存(缓存池)中的对象,而不是重新创建一个对象。


基本数据类型的类型转换


实线表示自动转换时不会造成数据丢失,虚线则可能会出现数据丢失问题。
注:

  1. 基本数据类型中,布尔类型boolean占有一个字节,由于其本身所代表的特殊含义,boolean类型与其他基本类型不能进行类型的转换(既不能进行自动类型的提升,也不能强制类型转换), 否则,将编译出错。

  2. 在Java中,整数类型(byte/short/int/long)中,对于未声明数据类型的整形,其默认类型为int型。在浮点类型(float/double)中,对于未声明数据类型的浮点型,默认为double型。
    故float f = 3.4 是不正确的。
    3.4为双精度数(double),将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成 float f =3.4F;


运算

Java 的参数是以值传递的形式传入方法中,而不是引用传递。

在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。因此在方法中改变指针引用的对象,那么这两个指针此时指向的是完全不同的对象,一方改变其所指向对象的内容对另一方没有影响。

但是如果在方法中改变对象的字段值会改变原对象该字段值,因为改变的是同一个地址指向的内容。

Q&A

  1. Java 中应该使用什么数据类型来代表价格?
    如果不是特别关心内存和性能的话,使用BigDecimal,否则使用预定义精度的 double 类型。

  2. 怎么将 byte 转换为 String?
    可以使用 String 接收 byte[] 参数的构造器来进行转换,需要注意的点是要使用的正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也可能不同。

  3. Java 中怎样将 bytes 转换为 long 类型?
    String接收bytes的构造器转成String,再Long.parseLong

  4. 我们能将 int 强制转换为 byte 类型的变量吗? 如果该值大于 byte 类型的范围,将会出现什么现象?
    是的,我们可以做强制转换,但是 Java 中 int 是 32 位的,而 byte 是 8 位的,所以,如果强制转化是,int 类型的高 24 位将会被丢弃,byte 类型的范围是从 -128 到 127。

  5. 我能在不进行强制转换的情况下将一个 double 值赋值给 long 类型的变量吗?
    不行,你不能在没有强制类型转换的前提下将一个 double 值赋值给 long 类型的变量,因为 double 类型的范围比 long 类型更广,所以必须要进行强制转换。

  6. 3*0.1 == 0.3 将会返回什么?
    true 还是 false? false,因为有些浮点数不能完全精确的表示出来。

  7. int 和 Integer 哪个会占用更多的内存?
    Integer 对象会占用更多的内存。Integer 是一个对象,需要存储对象的元数据。但是 int 是一个原始类型的数据,所以占用的空间更少。

  8. Java 中 ++ 操作符是线程安全的吗?
    不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,然后存储回内存,这个过程可能会出现多个线程交差。还会存在竞态条件(读取-修改-写入)。(留至JVM中补充)

  9. a = a + b 与 a += b 的区别
    += 隐式的将加操作的结果类型强制转换为持有结果的类型。如果两个整数类型相加,如 byte、short 或者 int,首先会将它们提升到 int 类型,然后再执行加法操作。

条件循环(流程控制)(12个)

if

else

while

for

switch

case

default :在switch语句中,表示其中的一个分支

do

break

continue

return

instanceof : 用来测试一个对象是否是指定类型的实例对象

Q&A

  1. switch是否能作用在byte上,是否能作用在long上,是否能作用在String上

在Java5以前,switch(expr)中,expr只能是byte、short、char、int。从Java5开始,Java中引入了枚举类型,expr也可以是enum类型,从Java7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。

  1. break,continue,return的区别及作用
    break跳出上一层循环,不再执行循环(结束当前的循环体)
    continue跳出本次循环,继续执行下次循环(结束正在执行的循环,进入下一个循环条件)
    return程序返回,不再执行下面的代码(结束当前的方法,直接返回)

在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。

修饰方法、类、属性和变量(11个)

static

final

super

this

native : 原生方法(非Java实现)

strictfp : 用于限制浮点计算的精度和舍入

synchronized : 表明一段代码需要同步执行(修饰代码块)

transient : 声明不用序列化的成员域

volatile

enum

void

this

this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针

this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。如果在方法内部调用同一个类的其他方法,就不必使用this,直接调用即可。

只有当需要明确指出对当前对象的引用时,才需要使用this关键字

this的用法在java中大体可以分为3种:

  1. 普通的直接引用,this相当于是指向当前对象本身。

  2. 形参与成员名字重名,用this来区分

  3. 引用本类的构造函数

super

super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类

super也有三种用法:

  1. 普通的直接引用,super相当于是指向当前对象的父类的引用。

  2. 子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分。

  3. 引用父类构造函数

以一道题来阐述super与this的使用

链接:https://www.nowcoder.com/questionTerminal/43e8e12bc30e412e92340d11dff6077d
来源:牛客网

class Test {
    public static void main(String[] args) {
        System.out.println(new B().getValue());
    }
    static class A {
        protected int value;
        public A (int v) {
            setValue(v);
        }
        public void setValue(int value) {
            this.value= value;
        }
        public int getValue() {
            try {
                value ++;
                return value;
            } finally {
                this.setValue(value);
                System.out.println(value);
            }
        }
    }
    static class B extends A {
        public B () {
            super(5);
            setValue(getValue()- 3);
        }
        public void setValue(int value) {
            super.setValue(2 * value);
        }
    }
}

此处为笔者个人的理解过程:
通过new B()构造了一个B类的实例后,执行B类的构造方法:

  1. super(5); 即调用A类的构造方法 setValue(5),但是此处的setValue,非调用A类的setValue,应为B类的setValue 即此处应该为B.setValue(5) 再通过B的setValue,调用super.setValue(2 * value)
    此时Value的值为10。

  2. setValue(getValue()- 3); 先执行getValue,B类未重写getValue方法,所以此处调用A类中的getValue。 首先执行try代码块中的内容 value值+1变为11 返回,然后执行finally 调用this.setValue,此处的this,指的是B类而非A类,调用的是B类的setValue 输出第一个结果 22
    getValue的返回值为11 执行 B.setValue(11-3) value值变为 2*8 = 16。

  3. new B()执行完后,value为16 回看main方法,执行getValue方法, 同上 value值变为17返回 执行finally 输出第二个结果 17 * 2 = 34

  4. 最后输出返回值 第三个结果 17。即 结果为 22 34 17。

this & super 在构造方法中的区别

  • 调用super()必须写在子类构造方法的第一行, 否则编译不通过

  • super从子类调用父类构造, this在同一类中调用其他构造均需要放在第一行

  • 尽管可以用this调用一个构造器, 却不能调用2个

  • this和super不能出现在同一个构造器中, 否则编译不通过

  • this()、super()都指的对象,不可以在static环境中使用

  • 本质this指向本对象的指针。super是一个关键字

static

static的目的

  1. 只想为某特定域分配单一存储空间,而不去考虑究竟要创建多少对象,甚至根本就不创建任何对象。

  2. 希望某个方法不与包含它的类的任何对象关联在一起。也就是说,即使没有创建对象,也能够调用这个方法。

当声明一个事物是static时,就意味着这个域或方法不会与包含它的那个类的任何对象实例关联在一起。

static的特点

  1. 被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享。

  2. 在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只有类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。

  3. static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的。

  4. 被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即使没有创建对象,也可以去访问。

static应用场景

  1. 修饰成员变量
  • 静态变量: 又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它;静态变量在内存中只存在一份。

  • 实例变量: 每创建一个实例就会产生一个实例变量,它与该实例同生共死。

  1. 修饰成员方法
  • 静态方法:静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法,同时只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字。
  1. 静态代码块
    静态语句块在类初始化时运行一次。

  2. 修饰类【只能修饰内部类也就是静态内部类】

  3. 静态导包
    在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。(用得少)

final

final有什么用?

  • 用于修饰类、属性和方法

  • 被final修饰的类不可以被继承

  • 被final修饰的方法不可以被重写
    private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。

  • 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的

  • 对于基本类型,final使数值不变

  • 对于引用类型,final使引用不变,也就不能引用其他对象,但是被引用的对象本身是可以修改的

final finally finalize的区别

  1. finalize是一个方法,不属于Java关键字。

  2. finally一般用于try-catch代码块中,作为错误处理的关键字使用。

  3. final用于修饰类、变量、方法。

Q&A

  1. 所有的final修饰的字段都是编译期常量吗?
    不是所有的final修饰的字段都是编译期常量。

  2. blank final
    Java允许生成空白final,也就是说被声明为final但又没有给出定值的字段,但是必须在该字段被使用之前被赋值,这给予我们两种选择:

    • 在定义处进行赋值(这不叫空白final)

    • 在构造器中进行赋值,保证了该值在被使用前赋值。
      注: 如果字段由static和final修饰,仅能在定义处赋值,因为该字段不属于对象,属于这个类。

synchronized

作用:保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
实现原理:JVM基于进入和退出monitor对象来实现方法同步和代码块同步。(详细内容见博文《浅谈Java中15种锁的分析比较》)
synchronized的四种状态:(详细内容见博文《浅谈Java中15种锁的分析比较》)

在应用Sychronized关键字时需要把握如下注意点:

  • 一把锁只能同时被一个线程获取,没有获得锁的线程只能等待
  • 每个实例都对应有自己的一把锁(this),不同实例之间互不影响;例外:锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象公用同一把锁
  • synchronized修饰的方法,无论方法正常执行完毕还是抛出异常,都会释放锁

synchronized用法

  1. 对象锁
    包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)。

  2. 类锁
    指synchronized修饰静态的方法或指定锁为Class对象。
    概念(重要):Java类可能有很多个对象,但只有一个class对象。
    本质:所以所谓的类锁,不过是Class对象的锁而已。
    用法和效果:类锁只能在同一时刻被一个对象拥有。

synchronized与Lock
synchronized的缺陷

  • 效率低
    锁的释放情况少,只有代码执行完毕或者异常结束才会释放锁;试图获取锁的时候不能设定超时,不能中断一个正在使用锁的线程,相对而言,Lock可以中断和设置超时

  • 不够灵活
    加锁和释放的时机单一,每个锁仅有一个单一的条件(某个对象),相对而言,读写锁更加灵活

  • 无法知道是否成功获得锁
    相对而言,Lock可以拿到状态

使用Synchronized的注意事项

  • 锁对象不能为空,因为锁的信息都保存在对象头里

  • 作用域不宜过大,影响程序执行的速度,控制范围过大,编写代码也容易出错

  • 避免死锁

  • 在能选择的情况下,既不要用Lock也不要用synchronized关键字,用java.util.concurrent包中的各种各样的类,如果不用该包下的类,在满足业务的情况下,可以使用synchronized关键,因为代码量少,避免出错

多线程访问同步方法的七种情况

  1. 两个线程同时访问一个对象的同步方法。

  2. 两个线程访问的是两个对象的同步方法。(synchronized不起作用)

  3. 两个线程访问的是synchronized的静态方法。

  4. 同时访问同步方法与非同步方法。(synchronized只作用于指定的方法)

  5. 访问同一个对象的不同的普通同步方法。

  6. 同时访问静态synchronized和非静态synchronized方法。(同时执行,指定的锁对象,不是同一个锁对象)

  7. 方法抛异常后,会释放锁。(不需要手动释放锁)

7种情况总结:3点核心思想

  1. 一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待。(1、5)
  2. 每个实例都对应有自己的一把锁,不同实例之间互不影响;例外:锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象共用同一把类锁。(2、3、4、6)
  3. 无论是方法正常执行完毕或者方法抛出异常,都会释放锁。

volatile

volatile被喻为轻量级的“synchronized”,它只是一个变量修饰符,只能用来修饰变量不能修饰方法和代码块。作用在于保证多线程之间的可见性
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

什么是可见性:
一个线程修改了某一个共享变量的值时,其他线程是否能够立刻知道这个修改。
对于串行程序来说,可见性问题是不存在的。
在并行程序中,如果一个线程修改了某一个全局变量,那么其他线程未必可以马上知道这个改动。多核时代,每颗CPU都有自己的缓存,这时CPU缓存与内存的数据一致性就没那么容易解决了,一个CPU缓存中的变量对另一个CPU是不可见的。

在JVM底层volatile是采用“内存屏障”来实现的。加入volatile关键字时,会多出现一个lock前缀指令,lock前缀指令实际上相当于一个内存屏障(也称内存栅栏),内存屏障会提供3个功能:

  1. 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面,即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。

  2. 它会强制将对缓存的修改操作立即写入主存。

  3. 如果是写操作,它会导致其他CPU中对应的缓存行无效。

关于volatile更详细的说明,见博文:Java内存模型(JMM)&volatile

synchronized和volatile的区别是什么?
synchronized表示只有一个线程可以获取作用对象的锁,执行代码,阻塞其他线程。
volatile表示变量在CPU的寄存器中是不确定的,必须从主存中读取。保证多线程环境下变量的可见性,禁止指令重排序

区别:
synchronized可以作用于方法、对象;volatile只能作用于变量
synchronized可以保证线程间的有序性、原子性、可见性,volatile只保证了可见性和有序行,无法保证原子性
synchronized线程阻塞,volatile线程不阻塞

transient

什么是序列化?
序列化是将对象的状态信息转换为可以存储或传输的形式(即可传输的字节序列)的过程。

  1. 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

  2. transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的,变量如果是用户自定义类变量,则该类需要实现Serializable接口

  3. 被static关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。

Serializable 与 Externalizable 的区别?

Serializable 接口是一个序列化 Java 类的接口,以便于它们可以在网络上传输或者可以将它们的状态保存在磁盘上,是 JVM 内嵌的默认序列化方式,成本高、脆弱而且不安全。Externalizable 允许你控制整个序列化过程,指定特定的二进制格式,增加安全机制。

错误处理(6个)

异常关键字(5个)具体见文:《Java基础四---泛型、注解、异常、反射》
try:用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。

catch:用于捕获异常。catch用来捕获try语句块中发生的异常。

finally:fnally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。

throw:用于抛出异常。

throws:用在方法签名中,用于声明该方法可能抛出的异常。

assert:断言

保留关键字(2个)

goto

const

注:null、true、false在很多文章中都将其作为了关键字,但是实际上,true、false、null都不是java的关键字,同时虽然不是关键字但是照样也不能用作标识符。故Java中有50个关键字,53个标志符

posted @ 2021-03-06 23:31  cos晓风残月  阅读(135)  评论(0编辑  收藏  举报