学习Java基础 常见问题
基础
JDK、JDK、JRE的关系

Java基本数据类型

final作用
| final修饰 | 解释 |
|---|---|
| 类 | 不可以被继承 |
| 方法 | 不能被重写 |
| 变量 | 不能被改变,不可变值的是变量的引用,指向的内容可以改变 |
final finally finalize
| 区别 | 描述 |
|---|---|
| final | 如上解释 |
| finally | 一般作用在try-catch代码块中,一般用来存放一些关闭资源的代码 |
| finalize | 属于Object类的一个方法,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的最后判断。 |
static作用
| static作用 | |
|---|---|
| 1 | 变量或者方法是独立于该类的任何对象,被类的实例对象所共享。 |
| 2 | 该类被第一次加载的时候,就会去加载被static修饰的部分,但只有在类第一次使用时才进行初始化 |
| 3 | 在类加载的时候分配空间,以后创建类对象的时候不会重新分配 |
| 4 | 修饰的变量或者方法是优先于对象存在的 |
面向对象、面向过程
| 区别 | 优点 | 缺点 |
|---|---|---|
| 面向对象 | 易维护、易复用、易扩展 | 性能比面向过程低 |
| 面向过程 | 能比面向对象高,因为类调用时需要实例化,开销比较大 | 没有面向对象易维护、易复用、易扩展 |
面向对象三大特征
| 面向对象三大特征 | 解释 |
|---|---|
| 封装 | 隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性 |
| 继承 | 通过使用继承可以提高代码复用性。继承是多态的前提 |
| 多态 | 可以指向子类或具体实现类的实例对象。提高了程序的拓展性 |
String、StringBuffer、StringBuilder
| String | StringBuffer | StringBuilder | |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 可变 |
| 安全性 | 安全,因为final | 安全,因为加锁 | 不安全 |
| 适用 | 少量操作 | 多线程+大量操作 | 单线程+大量操作 |
Int和Integer的区别
// 问题1:Integer和int比较
Integer a = new Integer(3);
Integer b = 3;
System.out.println(a == b);// false,引用类型和值类型不能比较
Integer d = new Integer(3);
System.out.println(a == d); // false,两个引用类型用==不能比较
int c = 3;
System.out.println(c == d); // true,Integer遇到int比较,Integer会拆箱成int做值比较
System.out.println("-------"); Integer底层提前缓存好了[-128,127]的值,所以创建两个范围的对象时,地址是一样的
// 问题2:Integer值返回缓存
Integer f1 = 100;
Integer f2 = 100;
System.out.println(f1 == f2);// true
Integer f3 = 129;
Integer f4 = 129;
System.out.println(f3 == f4);
System.out.println("-------");// false 原因:当一个Integer对象赋给int值的使用,调用Integer的valueOf方法
public static Integer valueOf(int i) {
// i在[-128,127]时,就会自动去引用常量池中的Integer对象,不会new新的
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
} Equals、==、hashCode区别
| == | equals | hashCode |
|---|---|---|
| 基本数据类型用,比较的是首地址值 | 引用类型用,比较是内容值 | 对象在内存中的地址,算出对象的哈希码值,并将其返回,重写equals方法,必须重写hashCode,因为集合类是通过HashCode判断重复的 |
序列化类中有一个不可序列化的对象
给该类设置关键字transiient告诉JDK不可被序列化
元注解
Java中使用返回值类型@interface表示该类是一个注解配置类,注解配置不能使用class、interface、abstract修饰
// 自定义注解,以下只是简单的演示。实际开发注解还需要一个注解处理器,自行百度学习
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface FruitProvider {
public int id() default -1;
public String name() default "";
public String address() default "";
} public class Apple {
// 使用自定义注解
@FruitProvider(id = 1, name = "红富士", address = "北京市")
private String appleProvider;
public String getAppleProvider() {
return appleProvider;
}
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
} 四大元注解:目标、保留、文档、继承
@Target:说明注解对象修饰范围

@Retention:该注解保留时长
- source :在源文件中有效, 源文件中就保留
- class:字节码文件中有效, 即在Class文件中被保留
- runtime:在运行时有效,即在运行时被保留
@Documented:表示被Javadoc文档工具化,只是一个标记注解,没有成员
@Inherited:表示该类型是被继承的,表名该注解类可以作用于其子类
Java的面向对象
| 名称 | 概念 |
|---|---|
| 封装 | 将事物封装成一个类,减少耦合,隐藏细节。保留特定接口和外部联系 |
| 继承 | 从已知的类中派生出一个新的类,可以通过覆盖/重写增强功能 |
| 多态 | 本质是一个程序中存在多个同名的不同方法 |
封装:将事物封装成一个类,减少耦合,隐藏细节。保留特定接口和外部联系
继承:从已知的类中派生出一个新的类,可以通过覆盖/重写增强功能
- Java中类的初始化顺序:
- 父类静态成员变量、静态代码块;子类静态成员变量、静态代码块
- 父类普通成员变量和代码块,再执行父类构造方法
- 子类普通成员变量和代码块,再执行父类构造方法
- 子类特点:
- 父类非private的属性和方法
- 添加自己的方法和属性,对父类进行扩展
- 重新定义父类的方法=方法的覆盖/重写
多态:本质是一个程序中存在多个同名的不同方法
子类的覆盖实现
方法的重载实现
子类作为父类对象使用
什么方法重载、方法重写?
重载(overload):一个类中存在多个同名的不同方法,这些方法的参数个数、顺序和类型不同均可以构成方法重载
重写(override):子类对父类非私有方法的重新编写,涉及写的就会有子父类
如果只有方法返回值不同,可以构成重载吗?
不可以,因为我们调用某个方法,并不关心返回值。编译器根据方法名和参数无法确定我们调用的是那个方法。
Java中有goto关键字吗
goto和const是Java中的保留字,现在未使用,未使用的原因是保证程序的可读性,并且避免使用beak+lebel带标签的语句
| 跳出循环方式1 | 跳出循环方式2 | 跳出循环方式3 |
|---|---|---|
| break+label,不推荐使用 | flag+break | throw new 抛出异常 |
抽象类和接口的
| 相同点 | 不同点 |
|---|---|
| 都不能直接实例化。 | 设计理念不同:抽象类是对对象/类的抽象,接口是对行为的抽象 |
| 都可以有默认的实现方法(JDK8以后才有的) | 抽象类只能单一继承,接口可以多重继承;抽象类可以有构造器,接口没有 |
| 都可以不需要实现类或者继承类去实现所有方法 | 抽象类中的抽象方法可以用public/protected/default abstract修饰;抽象类中的变量可以在子类中重新定义并赋值 |
| 如果要实例化,抽象类变量必须实现所有抽象方法,接口变量必须实现所有接口未实现的方法 | 接口的方法默认修饰符是public abstract,可以加statci关键字;接口中的变量默认是public static final,且必须给初值,在实现类中不能被重新定义和赋值 |
接口和抽象类该如何选择?
当我们仅仅只需要定义一些抽象方法而不需要额外的具体方法/变量的时候,用接口;反之,考虑抽象类
接口的普通方法、default修饰方法:
public interface MyInterface {
// 接口的普通方法只能等待实现类实现,不能具体定义
void test();
// 但是JDK8以后,接口可以default声明,然后具体定义
default void say(String message) {
System.out.println("hello:"+message);
}
} public class MyInterfaceImpl implements MyInterface {
// 实现接口里的抽象方法
@Override
public void test() {
System.out.println("test被执行");
}
// 当然也可以重写say方法
public static void main(String[] args) {
MyInterfaceImpl client = new MyInterfaceImpl();
client.test();
client.say("World");
}
} 执行结果:
test被执行 hello:World
如果实现类实现了两个接口,两个接口都有相同的(default)默认方法名,那么该方法重写会报错
解决办法:
- 实现类重写多个多个接口的默认方法
- 手动指定重写哪个接口的默认方法
public interface MyInterface {
void test();
default void say(String message) {
System.out.println("hello:"+message);
}
}
public interface MyInterface1 {
default void say(String message) {
System.out.println("hello1:" + message);
}
}
public class MyInterfaceImpl1 implements MyInterface, MyInterface1 {
@Override
public void test() {
System.out.println("test是普通方法被重写");
}
@Override
public void say(String message) {
// 方法一:System.out.println("实现类重写多个接口相同的默认方法:" + message);
// 方法二:指定重写哪个接口的默认方法
MyInterface.super.say(message);
}
public static void main(String[] args) {
MyInterfaceImpl1 client = new MyInterfaceImpl1();
client.say("好的");
}
} 执行结果:
实现类重写多个接口相同的默认方法:hello+好的
浅拷贝和深拷贝
| 浅拷贝 | 深拷贝 |
|---|---|
| 被复制的对象的所有变量都含有与原来对象相同的值,对拷贝后对象的引用依然指向原来的对象 | 不仅复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值 |
创建对象的方式
| 创建对象方式 | 是否调用构造器 |
|---|---|
| new + 类名 | 是 |
| Class.newInstance | 是 |
| Constructor.newInstance | 是 |
| Clone | 否 |
| 反序列化 | 否 |
值传递和引用传递
值传递:传递是一个对象副本,即使副本改变,也不会影响原对象
引用传递:传递不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到所有的对象上
public class Test {
public static void main(String[] args) {
int a = 1;
// 基本数据类型:值传递,原值不会变
change(a);
System.out.println(a);
}
private static void change(int num) {
num++;
}
} public class Test1 {
public static void main(String[] args) {
// 以下2个是引用传递,会改变原值
StringBuilder hello1 = new StringBuilder("hello1");
StringBuffer hello2 = new StringBuffer("hello2");
// String存放在常量池,虽然是引用传递,但不会改变原值
String hello3 = new String("hello3");
change1(hello1);
change2(hello2);
change3(hello3);
System.out.println(hello1);
System.out.println(hello2);
System.out.println(hello3);
}
private static void change3(String str) {
str += " world";
}
private static void change1(StringBuilder str) {
str.append(" world");
}
private static void change2(StringBuffer str) {
str.append(" world");
}
} public class Test3 {
public static void main(String[] args) {
StringBuffer hello = new StringBuffer("hello");
System.out.println("before:" + hello);
changeData(hello);
// 前后值:都没有发生改变
// 因为changeData中str形参重新执行了str1,与原值hello无关了
System.out.println("after:" + hello);
}
private static void changeData(StringBuffer str) {
StringBuffer str1 = new StringBuffer("Hi");
str = str1;
str1.append("world");
}
} public class PassTest {
public static void main(String[] args) {
int i = 1;
String str = "hello";
Integer num = 200;
int[] arr = {1, 2, 3, 4, 5};
MyData my = new MyData();
change(i, str, num, arr, my);
/*
结果:传值还是传引用?
i = 1 传值。基本数据类型不会变
str = hello 传常量池地址。字符串不变
num = 200,传堆中的地址。原包装类不变,和字符串一样
arr = [2, 2, 3, 4, 5] 传堆中数组的首地址。发生了改变
my.a = 11,传堆中地址,资源类变量发生改变。资源类中的变量,会在堆中生成一个实例
*/
System.out.println("i = " + i);
System.out.println("str = " + str);
System.out.println("num = " + num);
System.out.println("arr = " + Arrays.toString(arr));
System.out.println("my.a = " + my.a);
}
public static void change(int j, String s, Integer num, int[] arr, MyData myData) {
j += 1;
s += "world";
num += 1;
arr[0] += 1;
myData.a += 1;
}
}
class MyData {
int a = 10;
} 结果:
- 基本数据类型是值传递,不会改变原值
- String和包装类是引用传递,但不会改变原值,因为形参指向了另一个新生成的对象,原值不变
- 数组和自定义类时引用传递,会改变原值,因为数组是连续地址空间,没有在堆中新生成实例;自定义类中的成员变量分配在堆中,也没有重新生成实例
i = 1 // 传值。基本数据类型不会变 str = hello // 传常量池地址。字符串不变 num = 200 // 传堆中的地址。原包装类不变,和字符串一样 arr = [2, 2, 3, 4, 5]// 传堆中数组的首地址。发生了改变 my.a = 11// 传堆中地址,资源类变量发生改变。资源类中的变量,会在堆中生成一个实例
权限修饰符
| 作用域 | 当前类 | 同一包 | 子孙类 | 其他包 |
|---|---|---|---|---|
| public | 可以 | 可以 | 可以 | 可以 |
| protected | 可以 | 可以 | 可以 | 不可以 |
| default | 可以 | 可以 | 不可以 | 不可以 |
| private | 可以 | 不可以 | 不可以 | 不可以 |
反射机制
反射优缺点
| 反射优点 | 反射缺点 | 反射使用场景 |
|---|---|---|
| 装载到JVM中得的信息,动态获取类的属性方法等信息,提高语言灵活性和扩展性 | 性能较差,速度低于直接运行 | Spring等框架 |
| 提高代码复用率 | 程序的可维护性降低 | 动态代理 |
获取字节码
| 方式1 | 方式2 | 方式3 |
|---|---|---|
| 类名.class | 对象名.getClass() | Class.forName(classPath) |
public class User {
String username;
String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
} public class ThreeClassGetDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 方式一:类.class
Class<Integer> intClass = int.class;
// 方式二:对象.getClass()
User user = new User();
Class<? extends User> userClass = user.getClass();
// 方式三:Class.forName(类名)
String ClassName = "基础.反射.User";
Class<?> userClass1 = Class.forName(ClassName);
}
} public class UserClassDemo {
public static void main(String[] args) {
String className = "基础.反射.User";
try {
// 通过反射获取userClass
Class<?> userClassByForName = Class.forName(className);
// 获取构造器
Constructor<?> constructor = userClassByForName.getConstructor();
// 生成user实例
User user = (User) constructor.newInstance();
user.setUsername("张三");
user.setPassword("123");
System.out.println(user);
// 反射来修改成员变量
Class<? extends User> userClassByuser = user.getClass();
userClassByuser.getDeclaredField("username").set(user, "张三1");
userClassByuser.getDeclaredField("password").set(user, "456");
// 反射修改方法
Method setUsername = userClassByuser.getMethod("setUsername", String.class);
setUsername.invoke(user, "张三2");
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
}
}
} 打印结果:
User{username='张三', password='123'}
User{username='张三2', password='456'} 获取构造器等
| 获取构造器 | 获取成员变量 | 获取成员方法 | |
|---|---|---|---|
| 非私有 | getConstructor(类型.class) | getMethod(名, 参数.class); | getDeclaredField("id"); |
| 私有 | getDeclaredConstructor(类型.class)和setAccessible(true) | getDeclaredMethod(名, 参数.class);setAccessible(true) | getDeclaredField("id");setAccessible(true) |
定义测试的User:看到原类的构造器、成员属性、方法,想着怎么使用反射生成
package reflect;
public class User {
private int id=1;
private String name="张三";
private static Date date;
public User() {
}
public User(int id) {
this.id = id;
}
private User(String name) {
this.name = name;
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public void fun1() {
System.out.println("无参的fun1被调用");
}
public void fun2(int id) {
System.out.println("fun2:" + id);
}
public void fun3(int id, String s) {
System.out.println("fun3:" + id + "," + s);
}
private void fun4(Date date) {
System.out.println("fun4:" + date);
}
public static void fun5() {
System.out.println("fun5");
}
public static void fun6(String[] args) {
System.out.println(args.length);
}
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 static Date getDate() {
return date;
}
public static void setDate(Date date) {
User.date = date;
}
} public class Demo1 {
/**
* 获取非私有构造器
*/
@Test
public void test2() throws Exception {
Class<?> userClazz = Class.forName("reflect.User");
Constructor<?> c1 = userClazz.getConstructor();
Constructor<?> c2 = userClazz.getConstructor(int.class);
Constructor<?> c3 = userClazz.getConstructor(int.class, String.class);
User user = (User) c3.newInstance(1, "A");
System.out.println(user);
}
/**
* 获取私有构造器
*/
@Test
public void test3() throws Exception {
Class<?> userClazz = Class.forName("reflect.User");
// 私有需要declared修饰
Constructor<?> c = userClazz.getDeclaredConstructor(String.class);
// setAccessible设置暴露破解
c.setAccessible(true);
User user = (User) c.newInstance("A");
System.out.println(user);
}
/**
* 获取所有构造器:私有和非私有
*/
@Test
public void test4() throws Exception {
Class<?> userClazz = Class.forName("reflect.User");
Constructor<?>[] constructors = userClazz.getDeclaredConstructors();
for (Constructor c : constructors) {
System.out.println(c);
}
}
} 特殊情况:因为1.4是将字符数组分开作为小个体,String[]作为方法参数需要(Object)强转/new Object[]{包装}
public class Demo2 {
/**
* 获取非私有的成员方法
*/
@Test
public void test1() throws Exception {
Class<?> claszz = Class.forName("reflect.User");
User user = (User) claszz.newInstance();
Method fun1 = claszz.getMethod("fun1", null);
fun1.invoke(user, null);
Method fun2 = claszz.getMethod("fun2", int.class);
fun2.invoke(user, 1);
Method fun3 = claszz.getMethod("fun3", int.class, String.class);
fun3.invoke(user, 1, "A");
}
/**
* 获得私有方法
*/
@Test
public void test2() throws Exception {
Class<?> claszz = Class.forName("reflect.User");
User user = (User) claszz.newInstance();
// declared修饰private
Method fun4 = claszz.getDeclaredMethod("fun4", Date.class);
// setAccessible设置暴露破解
fun4.setAccessible(true);
fun4.invoke(user, new Date());
}
/**
* 获得无数组参数的静态方法
*/
@Test
public void test3() throws Exception {
Class<?> claszz = Class.forName("reflect.User");
Method fun5 = claszz.getDeclaredMethod("fun5");
fun5.invoke(null);
}
/**
* 特殊情况:获得String数组参数的静态方法
*/
@Test
public void test4() throws Exception {
Class<?> claszz = Class.forName("reflect.User");
Method fun6 = claszz.getDeclaredMethod("fun6", String[].class);
// fun6.invoke(null, new String[]{"1","2"}); 是要报错的,因为JDK4是把字符数组当做一个个对象解析
// 以下两种方式解决:
fun6.invoke(null, (Object) new String[]{"1", "2"});
fun6.invoke(null, new Object[]{new String[]{"1", "2"}});
}
} 一般来说成员属性都是私有的:getDeclaredField(setAccessible)后set值
public class Demo3 {
/**
* 获取非静态的私有成员变量
*/
@Test
public void test1() throws Exception {
Class<?> userClass = Class.forName("bean.User");
User user = (User) userClass.newInstance();
Field id = userClass.getDeclaredField("id");
id.setAccessible(true);
id.set(user, 2);
Field name = userClass.getDeclaredField("name");
name.setAccessible(true);
name.set(user, "李四");
System.out.println(user);
}
/**
* 获取静态成员变量
*/
@Test
public void test2() throws Exception {
Class<?> userClass = Class.forName("bean.User");
Field date = userClass.getDeclaredField("date");
date.setAccessible(true);
date.set(null, new Date());
System.out.println("User的Date:" + User.getDate());
}
} String.intern问题
public class StringQuestion {
/*
intern:返回值一个字符串,内容与此字符串相同,但一定取自具有唯一字符串的池
*/
public static void main(String[] args) {
String str1 = new StringBuilder("58").append("同城").toString();
System.out.println(str1);
System.out.println(str1.intern());
System.out.println(str1 == str1.intern());
System.out.println("-----------");
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2);
System.out.println(str2.intern());
System.out.println(str2 == str2.intern());
}
} 58同城 58同城 true ----------- java java false
第一个是true,第二个为什么是false?
因为JDK初始化sun.misc.Version会在常量池自动生成一个“Java”,与剩余生成的”Java“地址肯定不一样。其余字符串都是用户创建才会在常量池生成
public class Version {
private static final String launcher_name = "java";
private static final String java_version = "1.8.0_271";
private static final String java_runtime_name = "Java(TM) SE Runtime Environment";
private static final String java_profile_name = "";
...
} 异常分类
异常的概念:异常指在方法不能按照 常方式 ,可以通过抛出异常的方式退出 该方法,在异常中封装了方法执行 程中的错误信息及原因 调用 该异 常后可根据务的情况选择处理该异常或者继续抛出。
| 异常分类 | 概述 |
|---|---|
| Error | Java 程序运行错误 ,如果程序在启动时出现 Error 则启 动失败;如果程序在运行过程中出现 Error ,则系统将退出进程 |
| Exception | Java 程序运行异常,即运行中的程序发生了人们不期望发生的情况,可以被Java异常机制处理 |
| Exception分类 | 解释 | 常见 |
|---|---|---|
| RuntimeException | Java 虚拟机正常运行期间抛出的异常 | NullPointerException、ClassCastException ArraylndexOutOfBundsException |
| CheckedException | 编译阶段 Java 编译器会检查 CheckedException 异常井强调捕获 | IO Exception、SQLExcption、ClassNotFoundException |


捕获异常
public class ThrowException {
// 抛出异常的3种方式
// 1.throw:获取方法中的异常,throw 后面的语句块将无法被执行(finally除外)
private static void getThrow() {
String str = "str";
int index = 10;
if (index > str.length()) {
throw new StringIndexOutOfBoundsException("index > str.length");
} else {
System.out.println(str);
}
}
// 2.throws作用在方法上
private static int getThrows(int a, int b) throws Exception {
return a / b;
}
// 3.tryCatch包裹
private static void getTryCatch() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("最后必须会执行");
}
}
} 内部类
| 内部类 | 解释 |
|---|---|
| 静态内部类 | 可以访问外部类的静态变量和方法 |
| 成员内部类 | 非静态内部类,不能定义静态方法和变量(final除外) |
| 局部内部类 | 类中方法中定义的一个类 |
| 匿名内部类 | 继承一个父类或者实现一个接口的方式直接定义并使用的类 |
public class Outer {
private void test(final int i) {
new Service() {
public void method() {
for (int j = 0; j < i; j++) {
System.out.println("匿名内部类" );
}
}
}.method();
}
}
//匿名内部类必须继承或实现一个已有的接口
interface Service{
void method();
} 泛型标记

泛型上下限
| 泛型上下限 | 解释 |
|---|---|
| <? extends T> | ?是T的子类/T接口的子接口 |
| <? super T> | ?是T的父类/T接口的父接口 |
定义泛型
泛型类
泛型类的使用:类名后<?>
public class TDemo<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public static void main(String[] args) {
TDemo<Integer> tDemo1 = new TDemo<>();
TDemo<String> tDemo2 = new TDemo<>();
tDemo1.setValue(1);
System.out.println(tDemo1.getValue());
tDemo2.setValue("a");
System.out.println(tDemo2.getValue());
}
} 泛型方法
泛型方法使用:在方法返回值前定义泛型<?>,也可以继承一些接口<? extends Comparable<e>></e>
public static <E extends Comparable<E>> void bubbleSort0(E[] arr) {
// 只需要n-1层外部循环
for (int i = arr.length - 1; i > 0; i--) {
for (int j = 0; j < i && arr[j].compareTo(arr[j + 1]) > 0; j++) {
swap(arr, j, j + 1);
}
}
} 泛型接口
接口<?>,其实现类指定类型如implement 接口
public interface TInterfer<T> {
public T getId();
} public class TInterferImpl implements TInterfer<String> {
@Override
public String getId() {
return UUID.randomUUID().toString().substring(0, 3);
}
} 序列化
| 名词 | 关键字 | 特性 |
|---|---|---|
| 序列化 | implements Serializable | 底层字节数组,保存对象的状态信息,不能保存静态变量 |
| 反序列化 | transient | 被该关键字修饰的,不能被反系列化 |
查看10道真题和解析