java基础

什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决

一、序列化和反序列化的概念
序列化:把对象转换为字节序列的过程称为对象的序列化。
transient 修饰的属性,是不会被序列化的。
静态static的属性,他不序列化
当属性是对象的时候,对象也要实现序列化接口

反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)

二、什么情况下需要序列化
当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
当你想用套接字在网络上传送对象的时候;
当你想通过RMI传输对象的时候;

三、如何实现序列化
实现Serializable接口即可
实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值

原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。

有没有可能2个不相等的对象有相同的hashcode

hashCode是所有java对象的固有方法,如果不重载的话,返回的实际上是该对象在jvm的堆上的内存地址,而不同对象的内存地址肯定不同,所以这个hashCode也就肯定不同了。如果重载了的话,由于采用的算法的问题,有可能导致两个不同对象的hashCode相同。
而且,还需要注意一下两点:
1)hashCode和equals两个方法是有语义关联的,它们需要满足:
A.equals(B)==true --> A.hashCode()==B.hashCode()
因此重载其中一个方法时也需要将另一个也重载。
2)hashCode的重载实现需要满足不变性,即一个object的hashCode不能前一会是1,过一会就变成2了。hashCode的重载实现最好依赖于对象中的final属性,从而在对象初始化构造后就不再变化。一方面是jvm便于代码优化,可以缓存这个hashCode;另一方面,在使用hashMap或hashSet的场景中,如果使用的key的hashCode会变化,将会导致bug,比如放进去时key.hashCode()=1,等到要取出来时key.hashCode()=2了,就会取不出来原先的数据。这个可以写一个简单的代码自己验证一下。

这样的a.hashcode() 有什么用,与a.equals(b)有什么关系

hashcode()方法提供了对象的hashCode值,是一个native方法,返回的默认值与System.identityHashCode(obj)一致。
hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。
通常这个值是对象头部的一部分二进制位组成的数字,具有一定的标识对象的意义存在,但绝不定于地址。

作用是:用一个数字来标识对象。比如在HashMap、HashSet等类似的集合类中,如果用某个对象本身作为Key,即要基于这个对象实现Hash的写入和查找,那么对象本身如何实现这个呢?就是基于hashcode这样一个数字来完成的,只有数字才能完成计算和对比操作。

hashcode是否唯一
hashcode只能说是标识对象,在hash算法中可以将对象相对离散开,这样就可以在查找数据的时候根据这个key快速缩小数据的范围,但hashcode不一定是唯一的,所以hash算法中定位到具体的链表后,需要循环链表,然后通过equals方法来对比Key是否是一样的。

equals与hashcode的关系
equals相等两个对象,则hashcode一定要相等。但是hashcode相等的两个对象不一定equals相等。

在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题

早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。泛型主要针对向下转型时所带来的安全隐患

泛型好处
* 提高安全性(将运行期的错误转换到编译期)
* 省去强转的麻烦

其核心组成是在声明类或接口时,不设置参数或属性的类型。
泛型类型必须是引用类型
前后的泛型必须一致,或者后面的泛型可以省略不写(1.7的新特性菱形泛型)

泛型通配符<?>
* 任意类型,如果没有明确,那么就是Object以及任意的Java类了

  • B:? extends E
    • 向下限定,E及其子类
  • C:? super E
    • 向上限定,E及其父类

说一说你对java.lang.Object对象中hashCode和equals方法的理解。在什么场景下需 要重新实现这两个方法

hashCode是内存地址的十进制,equals比较对象的地址是否相等,toString是java.lang.Object@hashcode的十六进制
map和set中去重

在自己的代码中,如果创建一个java.lang.String类,这个类是否可以被类加载器加载?为什么

不会加载。
String应该是被Bootstrap ClassLoader加载了,所以App ClassLoader就不会再去加载我们写的String类了,导致我们写的String类是没有被加载的。
在这里插入图片描述

error和exception的区别,CheckedException,RuntimeException的区别

在这里插入图片描述
Error(错误)表示系统级的错误和程序不必处理的异常,是java运行环境中的内部错误或者硬件问题。比如:内存资源不足等。对于这种错误,程序基本无能为力,除了退出运行外别无选择,它是由Java虚拟机抛出的。

Exception(违例)表示需要捕捉或者需要程序进行处理的异常,它处理的是因为程序设计的瑕疵而引起的问题或者在外的输入等引起的一般性问题,是程序必须处理的。
Exception又分为运行时异常,受检查异常。
运行时异常,表示无法让程序恢复的异常,导致的原因通常是因为执行了错误的操作,建议终止程序,因此,编译器不检查这些异常。如果出现RuntimeException,那么一定是程序员的错误.
受检查异常,是表示程序可以处理的异常,也即表示程序可以修复(由程序自己接受异常并且做出处理), 所以称之为受检查异常。

Error和RuntimeException及其子类成为未检查异常(unchecked),其它异常成为已检查异常(checked)。

请列出5个运行时异常
ClassCastException(类转换异常)
StringIndexOutOfBoundsException字符串越界
IndexOutOfBoundsException(数组越界)
NullPointerException(空指针)
IllegalArgumentException参数异常
ArithmeticException算术异常类,int a=5/0,一个整数“除以零”时,抛出异常

深拷贝和浅拷贝区别

浅拷贝—能复制变量,如果对象内还有对象,则只能复制对象的地址。另一处修改,你当下的对象也会修改

深拷贝—能复制变量,也能复制当前对象的 内部对象

四种权限修饰符


private不能修饰类

如何在父类中为子类自动完成所有的hashcode和equals实现?这么做有何优劣

超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的。大多数的Set实现都从AbstractSet继承equals实现,List实现从AbstractList继承equals实现,Map实现从AbstractMap继承equals实现
AbstractMap重写了hashcode和equals方法,内部用了抽象方法entrySet(),子类Hashmap实现了这个抽象方法,判断key和value是否相等
AbstractList重写equals方法,比较每个元素是否相等,AbstractSet也是
hashcode和equals都是同时重写

类与接口的关系

1.类与类: 继承关系,只能单继承,可以多层继承
2.类与接口: 实现关系,可以但实现,也可以多实现
implements inter1,inter2,… 正确
implements inter1,implements innter2,… 错误
implements只能写一次
并且还可以在继承一个类的同时实现多个接口
3.接口与接口: 继承关系,可以单继承,也可以多继承。extends inter1,inter2。
同类的都是继承关系
关键字extends和implements都只能写一次


interface a{
    
}

interface b{
    
}

interface c extends a,b{
    
}

抽象类和接口的区别

1.成员区别
(1)抽象类,和普通类一样,只是比普通类多了一个功能,可以写抽象方法
成员变量: 可以是变量,也可以是常量
构造方法: 有
成员方法: 可以抽象,也可以非抽象
(2)接口:
成员变量: 只能是常量
成员方法: 只能是抽象
2.关系区别
(1)类与类: 继承关系,只能单继承,可以多层继承
(2)类与接口: 实现关系,可以但实现,也可以多实现
(3)接口与接口: 继承关系,可以单继承,也可以多继承
3.抽象类 被继承体现的是”is a”的关系,抽象类中定义的是该继承体系的共性功能
接口 被实现体现的是”like a”的关系,接口中定义的是该继承体系的扩展功能

final的用途

1、被final修饰的类不可以被继承

2、被final修饰的方法不可以被重写

3、被final修饰的变量不可以被改变

JAVA中的几种基本数据类型是什么,各自占用多少字节。

在这里插入图片描述
基本数据类型各占多少个字节:

数据类型 字节 默认值
byte 1 0
short 2  0
int 4 0
long 8 0
float 4 0.0f
double 8 0.0d
char 2 ‘\u0000’
boolean 4 false
关于boolean占几个字节,众说纷纭,虽然boolean表现出非0即1的“位”特性,但是存储空间的基本计量单位是字节,不是位。所以boolean至少占1个字节。
JVM规范中,boolean变量当作int处理,也就是4字节;而boolean数组当做byte数组处理,即boolean类型的数组里面的每一个元素占1个字节。

String类能被继承吗,为什么。

因为String类有final修饰符,而final修饰的类是不能被继承的。

String,Stringbuffer,StringBuilder的区别

Java String 类——String字符串常量
字符串广泛应用 在Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。
需要注意的是,String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间。我们来看一下这张对String操作时内存变化的图:
在这里插入图片描述
我们可以看到,初始String值为“hello”,然后在这个字符串后面加上新的字符串“world”,这个过程是需要重新在栈堆内存中开辟内存空间的,最终得到了“hello world”字符串也相应的需要开辟内存空间,这样短短的两个字符串,却需要开辟三次内存空间,不得不说这是对内存空间的极大浪费。为了应对经常性的字符串相关的操作,就需要使用Java提供的其他两个操作字符串的类——StringBuffer类和StringBuild类来对此种变化字符串进行处理。

三者的区别:
(1)字符修改上的区别(主要)
String:不可变字符串;
StringBuffer:可变字符串、效率低、线程安全;
StringBuilder:可变字符序列、效率高、线程不安全;
(2)初始化上的区别,String可以空赋值null,后者不行,报错

小结:(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder(推荐使用)。

讲讲类的实例化顺序

类加载器实例化时进行的操作步骤(加载–>连接->初始化)。
父类静态变量、
父类静态代码块、

子类静态变量、
子类静态代码块、

父类非静态变量(父类实例成员变量)、
父类实例代码块
父类构造函数、

子类非静态变量(子类实例成员变量)、
子类实例代码块
子类构造函数。

反射的原理,反射创建类实例的三种方式是什么

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

获取Class对象的三种方式:
★ 方式一
通过对象的getClass方法进行获取。这种方式需要具体的类和该类的对象,以及调用getClass方法。

★ 方式二
任何数据类型(包括基本数据类型)都具备着一个静态的属性class,通过它可直接获取到该类型对应的Class对象。这种方式要使用具体的类,然后调用类中的静态属性class完成,无需调用方法,性能更好。

★ 方式三
通过Class.forName()方法获取。这种方式仅需使用类名,就可以获取该类的Class对象,更有利于扩展。

类装载过程

装载:通过累的全限定名获取二进制字节流,将二进制字节流转换成方法区中的运行时数据结构,在内存中生成Java.lang.class对象;

链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;

校验:检查导入类或接口的二进制数据的正确性;(文件格式验证,元数据验证,字节码验证,符号引用验证)

准备:给类的静态变量分配并初始化存储空间;

解析:将常量池中的符号引用转成直接引用;

初始化:激活类的静态变量的初始化Java代码和静态Java代码块,并初始化程序员设置的变量值。

Class.forName和ClassLoader区别

Java中Class.forName和classloader都可以用来对类进行加载。

Class.forName除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
Class.forName(className)方法,内部实际调用的方法是 Class.forName(className,true,classloader);
第2个boolean参数表示类是否需要初始化, Class.forName(className)默认是需要初始化。
一旦初始化,就会触发目标对象的 static块代码执行,static参数也也会被再次初始化。

    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)

而classloader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
ClassLoader.loadClass(className)方法,内部实际调用的方法是 ClassLoader.loadClass(className,false);
第2个 boolean参数,表示目标对象是否进行链接,false表示不进行链接,由上面介绍可以,
不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行

protected Class<?> loadClass(String name, boolean resolve)

util包工具

Arrays
Base64
Collections
hashmap
List
Date
Optional
Random
Scanner
UUID

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值