1、java中==和equals和hashCode的区别?
== 用来比较 栈 中储存的内容,比如基本数据类比较、对象地址的比较;
equals用来比较 堆 中的内容,但分两种情况,当没有重写Object类的equals方法,还是比较对象地址,Object类源码equals 方法是this==obj比较的;当重写equals方法后就是先比较地址,在对值进行比较,可参考String类的equals方法源码写法;
hashCode是 集合 中用来比较两个对象hash码是否相等的,是Object类在对象地址基础上通过特定算法生成的各不相同的hash码,在集合中比较效率高,只需要直接寻找集合特定物理位置即可,不用循环比较
对象equals相等,hashCode一定相等;对象hashCode相等,equals可能相等,可能不等;
2、int、char、long各占多少字节数
Java 中:
1字节: byte , boolean
2字节: short , char
4字节: int , float
8字节: long , double
注:1字节(byte)=8位(bits)
3、int与Integer的区别
类型:int为基本类型,Integer为int包装类,初始值int为0,Integer为null;
拆/装箱:如Integer i = 1;int ii = 1; i==ii (1先装箱赋值给Integer,再拆箱为int和ii进行比较)
缓存:对于-128——127之间的数,Integer会进行缓存,当有一样的数时就会从缓存中取,
比如:Integer a = 127;Integer b = 127; a==b; 返回true
Integer a = 128;Integer b = 128; a==b; 返回false
Integer a = 127实际上被转化成了Integer a = Integer.valueOf(127);在valueOf函数中对符合条件的进行了缓存
4、对java多态的理解
多态实际上就是父类引用指向子类对象,这样多态对象就同时可以调用父类未被重写的方法和子类重写的方法;如下
class A ...{
public String show(D obj)...{ //方法1
return ("A and D");
}
public String show(A obj)...{ //方法2
return ("A and A");
}
}
class B extends A...{
public String show(B obj)...{ //方法3
return ("B and B");
}
public String show(A obj)...{ //方法4
return ("B and A");
}
}
class C extends B...{}
class D extends B...{}
调用:
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a2.show(b));
System.out.println(a2.show(c));
System.out.println(a2.show(d));
根据上述理论,a2多态可以调用方法1和方法4,并不能调用方法3和2,方法4重写方法2,故方法2不能被调用,多态进行了向上转型,不能调用子类中父类没有的方法,故不能调用方法3;因此答案为:
B and A
B and A
A and D
5、String、StringBuffer、StringBuilder区别
String 为字符串常量,StringBuffer、StringBuilder为字符串变量,String在进行读写的时候常量必须依靠新建对象,读写速度低于后两者,然而在StringBuffer类中有很多synchronized同步关键字处理线程同步问题,故String和StringBuffer均为线程安全的类,而StringBuilder没有对多线程进行处理,故速度最快,但只能运用于单线程模型中
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
6、什么是内部类?内部类的作用
内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类主要有以下几类:成员内部类、局部内部类、静态内部类、匿名内部类
作用:
- 内部类方法可以访问该类定义所在的作用域的数据,包括私有的数据
- 内部类可以对同一个包中的其他类隐藏起来,一般的非内部类,是不允许有 private 与protected权限的,但内部类可以
- 可以实现多重继承,比接口实现多继承更为方便,因为实现接口必须实现里面所有方法,一个类中可以多个内部类继承不同类实现不同功能,再在外部类方法通过内部类对象调用相应的功能即可。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷
详细介绍
成员内部类:在一个类中直接定义的内部类, 成员内部类与普通的成员没什么区别,可以与普通成员一样进行修饰和限制。成员内部类不能含有static的变量和方法
public class Outer {
private static int i = 1;
private int j = 10;
private int k = 20;
public static void outer_f1() {}
public void outer_f2() {}
// 成员内部类中,不能定义静态成员
// 成员内部类中,可以访问外部类的所有成员
class Inner {
// static int inner_i = 100;//内部类中不允许定义静态变量
int j = 100; // 内部类和外部类的实例变量可以共存
int inner_i = 1;
void inner_f1() {
System.out.println(i);
// 在内部类中访问内部类自己的变量直接用变量名
System.out.println(j);
// 在内部类中访问内部类自己的变量也可以用this.变量名
System.out.println(this.j);
// 在内部类中访问外部类中与内部类同名的实例变量用外部类名.this.变量名
System.out.println(Outer.this.j);
// 如果内部类中没有与外部类同名的变量,则可以直接用变量名访问外部类变量
System.out.println(k);
outer_f1();
outer_f2();
}
}
// 外部类的非静态方法访问成员内部类
public void outer_f3() {
Inner inner = new Inner();
inner.inner_f1();
}
// 外部类的静态方法访问成员内部类,与在外部类外部访问成员内部类一样
public static void outer_f4() {
// step1 建立外部类对象
Outer out = new Outer();
// step2 根据外部类对象建立内部类对象
Inner inner = out.new Inner();
// step3 访问内部类的方法
inner.inner_f1();
}
public static void main(String[] args) {
//outer_f4();//该语句的输出结果和下面三条语句的输出结果一样
// 如果要直接创建内部类的对象,不能想当然地认为只需加上外围类Outer的名字,
// 就可以按照通常的样子生成内部类的对象,而是必须使用此外围类的一个对象来
// 创建其内部类的一个对象:
// Outer.Inner outin = out.new Inner()
// 因此,除非你已经有了外围类的一个对象,否则不可能生成内部类的对象。因为此
// 内部类的对象会悄悄地链接到创建它的外围类的对象。如果你用的是静态的内部类,
// 那就不需要对其外围类对象的引用。
Outer out = new Outer();
Outer.Inner outin = out.new Inner();
outin.inner_f1();
}
}
局部内部类:在方法中定义的内部类称为局部内部类。与局部变量类似,局部内部类不能有访问说明符,因为它不是外围类的一部分,但是它可以访问当前代码块内的常量,和此外围类所有的成员。
需要注意的是:
(1)、局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
(2)、局部内部类对象不能使用该内部类所在方法的非final局部变量。
public class Outer {
private int s = 100;
private int out_i = 1;
public void f(final int k) {
final int s = 200;
int i = 1;
final int j = 10;
// 定义在方法内部
class Inner {
int s = 300;// 可以定义与外部类同名的变量
// static int m = 20;//不可以定义静态变量
Inner(int k) {
inner_f(k);
}
int inner_i = 100;
void inner_f(int k) {
// 如果内部类没有与外部类同名的变量,在内部类中可以直接访问外部类的实例变量
System.out.println(out_i);
// 可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的
System.out.println(j);
// System.out.println(i);
// 如果内部类中有与外部类同名的变量,直接用变量名访问的是内部类的变量
System.out.println(s);
// 用this.变量名访问的也是内部类变量
System.out.println(this.s);
// 用外部类名.this.内部类变量名访问的是外部类变量
System.out.println(Outer.this.s);
}
}
new Inner(k);
}
public static void main(String[] args) {
// 访问局部内部类必须先有外部类对象
Outer out = new Outer();
out.f(3);
}
}
静态内部类:如果你不需要内部类对象与其外围类对象之间有联系,那你可以将内部类声明为static。这通常称为嵌套类(nested class)。想要理解static应用于内部类时的含义,你就必须记住,普通的内部类对象隐含地保存了一个引用,指向创建它的外围类对象。然而,当内部类是static的时,就不是这样了。嵌套类意味着:
1. 要创建嵌套类的对象,并不需要其外围类的对象。
2. 不能从嵌套类的对象中访问非静态的外围类对象。
public class Outer {
private static int i = 1;
private int j = 10;
public static void outer_f1() {}
public void outer_f2() {}
// 静态内部类可以用public,protected,private修饰
// 静态内部类中可以定义静态或者非静态的成员
private static class Inner {
static int inner_i = 100;
int inner_j = 200;
static void inner_f1() {
// 静态内部类只能访问外部类的静态成员(包括静态变量和静态方法)
System.out.println("Outer.i" + i);
outer_f1();
}
void inner_f2() {
// 静态内部类不能访问外部类的非静态成员(包括非静态变量和非静态方法)
// System.out.println("Outer.i"+j);
// outer_f2();
}
}
public void outer_f3() {
// 外部类访问内部类的静态成员:内部类.静态成员
System.out.println(Inner.inner_i);
Inner.inner_f1();
// 外部类访问内部类的非静态成员:实例化内部类即可
Inner inner = new Inner();
inner.inner_f2();
}
public static void main(String[] args) {
new Outer().outer_f3();
}
}
生成一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类(正常情况下,你不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分,因为它是static 的。只是将嵌套类置于接口的命名空间内,这并不违反接口的规则)
匿名内部类:简单地说:匿名内部类就是没有名字的内部类。什么情况下需要使用匿名内部类?如果满足下面的一些条件,使用匿名内部类是比较合适的:
- 只用到类的一个实例。
- 类在定义后马上用到。
- 类非常小(SUN推荐是在4行代码以下)
- 给类命名并不会导致你的代码更容易被理解。
public class Parcel8 {
// Argument must be final to use inside
// anonymous inner class:
public Destination dest(final String name, String city) {
return new Destination(name, city) {
private String label = name;
public String getName() {
return label;
}
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Destination d = p.dest("Tanzania", "gz");
}
abstract class Destination {
Destination(String name, String city) {
System.out.println(city);
}
abstract String getName();
}
}
7、抽象类和接口区别
接口和抽象类都是对具体事物的一般化,让程序能够有更强的通用性,java只允许单继承,但却可以多实现,所以,接口在一定程度上时对抽象类的补充,区别如下图:
8、抽象类的意义
1,为子类提供一个公共的类型
2,封装子类中重复内容(成员变量和方法)
3,定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的
9、抽象类与接口的应用场景
1)抽象类和接口类的实例化,通过多态性(向上转型,向下转型)。
2)抽象类表示一个模板,接口表示一个标准。
3)常见的设计模式:模板设计,工厂设计,代理设计,适配器设计。
在开发中,一个类永远不要去继承一个已经实现好的类,要么继承抽象类,要么实现接口,如果两个类同时都
可以使用的话,优秀使用接口,避免单继承的局限。
10、抽象类是否可以没有方法和属性?
可以
11、接口的意义
制定协议规范、使用向上/下转型达到代码松耦合实现,便于管理
12、泛型中extends和super的区别
extends T>限定参数类型的上界:参数类型必须是T或T的子类型
super T> 限定参数类型的下界:参数类型必须是T或T的超类型
13、父类的静态方法能否被子类重写
不能,静态方法无法重写,而称为隐藏,在多态下进行调用任然调用的是父类的方法
14、进程和线程的区别
进程:系统资源分配的最小单位,有独立的内存空间,创建、切换、通信开销大
线程:系统调度的最小单位,没有独立的内存空间,有自己的栈、变量、程序计数器等,创建、切换、通信开销小
一个进程可以拥有多个线程,而一个线程只能属于一个进程
15、final,finally,finalize的区别
这三者没啥联系,final 修饰常量、类和方法表示不能再次更改;finally 是捕获异常时最后必须走的一步;finalize 时垃圾回收GC前做垃圾清理工作的。
16、序列化的方式
Serializable 和Parcelable
17、Serializable 和Parcelable 的区别
Serializable:java中带的,通过磁盘I/O进行读写实现,效率低
Parcelable:android系统提供的序列化方式,直接通过内存进行读取,相比Serializable 效率更高,Parcelable实现方式复杂一点,可以IDE插件自动序列化代码
18、静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
可以被继承,但不能被重写;子类继承父类中静态属性和静态方法称之为隐藏,原因就是相比于非静态方法,静态方法不存在隐含参数,它的调用时直接通过父类.方法名直接调用的;对于非静态方法和属性,有一个隐含的传入参数:对象实例在stack中的地址指针,所以每次调用时都要new 一个对象才能进行方法调用
总结一下class文件加载静态和非静态过程:
当一个class文件被ClassLoader load进入JVM后,方法指令保存在stack中,此时heap区没有数据。然后程序技术器开始执行指令,如果是静态方法,直接依次执行指令代码,当然此时指令代码是不能访问heap数据区的;如果是非静态方法,由于隐含参数没有值,会报错。因此在非静态方法执行前,要先new对象,在heap中分配数据,并把stack中的地址指针交给非静态方法,这样程序技术器依次执行指令,而指令代码此时能够访问到heap数据区了。
19、静态内部类的设计意图
静态内部类与非静态内部类之间存在一个最大的区别:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。
没有这个引用就意味着:
它的创建是不需要依赖于外围类的。
它不能使用任何外围类的非static成员变量和方法。
20、成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用
答案在6题中已给出
21、谈谈对kotlin的理解
kotlin 已成为官方开发语言 ,Android studio3.0及以后也会不断支持,所以在Android中重要性很高。
kotlin语言代码比Java简洁,文件名以.KT结尾,函数式编程
kotlin语言也有其局限,小众语言,在真实项目稳定性待考证,遇到坑资料比较少
22、闭包和局部内部类的区别
局部内部类是在方法中定义的内部类,没有修饰符。通过外部类新建内部类对象调用其方法。
闭包是一种能被调用的对象,它保存了创建它的作用域的信息,java中以接口+内部类实现。通过返回一个闭包对象:一个接口实例进行内部对象的调用
23、string 转换成 integer的方式及原理
Integer.valueOf(str) 里面包含了Integer.parseInt(str); 在方法内部去多次判断str是否符合转换标准,比如非空、长度在取值范围等,然后进行分割成char字符,单独算出每个的值,最后进行计算拼接计算。