问题来源如下链接,根据自己理解以及查找资料写的答案,有的地方自己也不是很清楚,欢迎多多指正。
1. Java中 == 、equals 和 hashCode 的区别
- == 如果是基本类型比较的是真实值,如果是引用类型比较的是内存中的地址
- Object类的equals方法内部也是调用==
public boolean equals(Object obj){
return (this==obj);
}
所以说如果子类不覆盖equals方法,== 和 equals是一样。
那为什么还有equals方法呢,我开发中的需求不同,比如:
String s1 = "hello";
String s2 = new String("hello");
由于s1在字符串常量池而s2在堆内存中,所以s1==s2肯定是false。
但是我们期望的是内容如果相同,我们就希望返回true,所以String类中重写了equals方法。
此时equals方法不再等同于==,而是根据我们自己的逻辑,进行判断。
- hashCode是Object类中的一个方法,它返回一个整形数,它怎样计算返回这个整数我不清楚,但是它不是对象的内存地址值。
那这个hashCode到底是做什么用的呢?
先来说几条java的通用约定:
- 重写equals方法的类,也必须重写hashCode方法
- 一个应用程序,一次运行中,在没有修改equals方法中的比较操作,多次调用一个对象hashCode方法,一定返回相同的值;多次运行,hashCode值则不一定相同
- 如果俩个对象x.equals(y) 返回true,则xy的hashCode值一定相等
- 如果俩个对象x.equals(y)返回false,则xy的hashCode值不一定不相等
这个得说一种数据结构散列,比如HashMap,HashSet,Hashtable都是基于散列的集合,其实这种技术的核心就是:
// index 元素存储位置也就是散列地址
// key 关键字
// fun 是一个函数,理想状态下不同的key返回的值不一样
index = fun(key)
其实这个fun就是hashCode函数,比如我们有下面的元素要存储,计算后的散列地址,以及在内存中的简单分配。基于散列的集合都要依赖hashCode这个方法。
![](https://img.haomeiwen.com/i1928711/c54699ed57bbf1de.png)
![](https://img.haomeiwen.com/i1928711/c4e74022561e5db5.png)
2. int ,char,long各占多少字节
byte(1) ,short(2),int(4), long(8), float(4) ,double(8),char (2),boolean(1)
3.int 和Integer的区别
- int 是基本数据类型,而Integer是int的包装类
- Integer必须实例化后才能使用,而int型变量不需要
- Integer的默认值是null,int是0
- Integer实际是对象的引用,当new一个Integer时,实际生成一个指针指向该对象,而int是直接存储数据值
延伸:
- 两个new出来的Integer,用==比较,永远为false,对象地址不同
- int和Integer比较时,只要值相等即相等,因为比较时会拆箱,将Integer转为int
Integer i = new Integer(100)
int j = 100
i==j 返回true,自动拆箱 - 一个new的Integer和一个非new的Integer比较,返回false,因为一个是堆的地址,一个指向常量池
Integer i = new Integer(100)
Integer j = 100
i==j 返回false - 在-128~127之间的俩个相同Integer比较时,会返回true,如果超出范围就会返回false
4. 谈谈java多态
多态就是同一个行为具有不同的表现形式,就是一个接口的方法,在不同的实例中执行操作不同
5.String,StringBuffer,StringBuilder区别
- String 字符串常量,不能修改
- StringBuffer字符串变量,可以修改,线程安全
- StringBuilder字符串变量,可以修改,线程不安全
6. 什么是内部类,内部类的作用
在一个类内部创建的类就叫内部类,内部类可以写在方法里,叫局部内部类。内部可以用static修饰,就是静态内部类。静态内部类和非静态内部类的区别在于,非静态内部类持有对外部类的引用,而静态内部类没有。
非静态内部类里面不允许有静态变量、方法、或者另一个静态内部类,而静态内部类里面可以有这一切
内部类的作用:
- 内部类可以很好的实现隐藏,体现了java的封装性
- 可以不用修改接口,实现同一个类中存在俩个同名同参数方法
- 可以实现多继承
- 内部类拥有外部类的所有元素的访问权限
重点说下作用3,可以实现多继承,这也是内部类存在的最直接因素。如果没有内部类,我们用实现接口的方式达到多继承效果。但是实现接口,就必须实现所有的方法,而内部类就可以有选择。
比如有类A,B, 类C中有两个内部类,D继承A,E继承B,这样我们实例化
C c = new C();
然后我们就可以调用D E中的方法了,也就会间接调到A B中的方法。
7. 接口和抽象类的区别
抽象类,被abstract修饰的类为抽象类,抽象类不能被实例化,只能被子类继承。抽象类中可以没有抽象方法,抽象类中可以有默认实现的方法。非抽象子类继承抽象类,必须要重写父类的抽象方法。
接口,内的方法不能有默认实现,必须为抽象方法,接口不能被继承只能被实现,实现类必须实现接口的所有方法。抽象类可以实现接口,非抽象子类中要实现费抽象和接口中的抽象方法。
很多时候他们甚至可以互换使用,为什么要有两个类似的东东呢,最重要的点在于类只能单继承,而可以多实现。实际开发中如果某个类继承了一个抽象类,就不能再继承其他类,有一定局限性,所以一般多用接口。
8.泛型中extends和super的区别
先说下泛型的常用俩种方法<T> 和 <? extends T> or <? super T>
<T>是将数据类型参数化,举个简单例子,定义一个带泛型的盘子类和不带泛型苹果类,盘子可以盛各种水果。
public class Plate<T> {
private T something;
public Palte(T t){
this.something = t;
}
private void setSomething(T t){
this.something = t;
}
private T getSomething(){
return something;
}
}
publci static void main(String[] args){
//盛苹果的盘子
Plate<Apple> plate = new Palte<>(new Apple());
}
在来举个例子,何种情况下使用 <?extends T> (上边界限定通配符) 和 <? super T> (下边界限定通配符)
//确定上边界
List<? extends Number> list;
// 不确定List存储哪种类型,但是肯定是Number的子类
list = new ArrayList<Integer>();
list = new ArrayList<Long>();
//确定下边界
List<? super Integer> list;
list = new ArrayList<Interge>();
list = new ArrayList<Number>();
9.父类的静态属性和方法能否继承?能否被重写?为什么?
子类可以继承父类的静态属性和静态方法,不能被重写
因为静态属性和静态方法初始化类的同时就会在内存中为其开辟了一块空间,并且是不能修改的,子类中可以有同名同参的静态或非静态方法,也可以有同名静态属性,但那不是从父类继承来的,而是自己属性或方法,也有自己的内存空间。
10.进程和线程的区别
几乎任何的操作系统都支持操作多个任务,通常一个任务就是一个程序,而一个程序就是一个进程。当一个进程运行时,内部可以包含多个顺序执行流,每个流就是一个线程。
进程是指运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个单位。
进程拥有以下特点:
- 独立行:进程是系统中独立存在的实体,它可以独立拥有资源,每一个进程都有自己独立的内存空间地址
- 动态性:进程和程序的区别在于进程是动态的,进程有时间概念,进程有自己的生命周期
- 并发性:多个进程可以在单个处理并发执行,互不影响
线程是进程的组成部分,一个进程可以拥有多个线程,但是一个线程只能拥有一个父进程。线程可以拥有自己的堆栈,程序计数器,局部变量,但不能独立拥有系统资源,它与父进程的其他线程共享父进程的资源。
11.final ,fianlly ,和finalize的区别
- final 修饰属性,方法,和类,修饰属性不能被修改,修饰的方法不能被重写,修饰的类不能被继承
- finally 通常是用来在常处理中
try{
//正常
}catch(Exception e){
//异常
}finally{
//总会执行
}
- finalize 是Object类中的一个方法,是垃圾回收器工作时,会调用将要被清理的对象finalize方法,以确保此对象没有其他任何引用指向它。
12. 简述java序列化和反序列化,并说明Serializable和Parcelable的区别
java序列化,简单的说就是将对象以一连串字节描述的过程,反序列化就是将这些字节重建成相同对象的过程。
再通俗一点:
![](https://img.haomeiwen.com/i1928711/863ff68eee5f95a1.png)
![](https://img.haomeiwen.com/i1928711/d0b427bc9f9efe83.png)
为什么会有序列化这种技术呢,因为这种机制允许你将对象转化成字节流后在网络上传播,并可以随时把对象持久化到数据库,文件等一系列读写操作。
虽然Serializable和Parcelable都能实现对象序列化,但是Serializable的作用是保存对象的属性到本地文件,数据库,网络流以方便数据传输,这种传输可以是程序内的,也可以是俩个程序之间的。而Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。
Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据,而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable。
网友评论