摘要:
最近在整理Android岗位面试题的答案,虽然工作已有两年,独立开发了好几个APP,但在不查资料的情况下,回答这些试题非常的困难,瞬间感觉一万点伤害。即使不为了找工作,整理这样一份Android面试题答案,也可以加深对各个知识点的理解,这一整套面试题基于《最全的BAT大厂面试题整理》,然后将每一个分类拆分成附带参考答案的独立的文章,在此非常感谢整理试题的原作者。
Android学习笔记七:Java源码深入学习(25号更新)
Android学习笔记八:Java常用数据结构(26号更新)
Android学习笔记九:Java线程、多线程和线程池(27号更新)
Note that:文章中给出的答案尽管参考了网上的很多资料,但肯定回答得还不够到位,更需要在实际的环境中运用、验证(求内推哈)
- Java基本数据类型及各自所占的字节数
类型 | 字节 |
---|---|
byte | 1个字节 |
short | 2个字节 |
int | 4个字节 |
long | 8个字节 |
float | 2个字节 |
double | 4个字节 |
chart | 2个字节 |
boolean | 视具体编译环境而定 |
- java中==和equals和hashCode的区别
两个基本数据类型使用==
,比较的是两个变量值;两个对象使用==
,比较的是两个对象的内存地址
使用类继承自Object基类,Object类定义了equals()
方法和hasCode()
方法,前者的实现调用==
进行比较,开发者通过重写这两个方法实现自定义比较的逻辑。
重写equals()
方法,必须重写hasCode()
方法,两个数值或对象通过equal比较返回true,那么这两个数值或对象的hasCode值一样;反之,两个数值或对象的hasCode值一样,两个数值或对象的equal不一定为true
- int与integer的区别
int,是一种基本的数据类型,存储的是一个数值,默认值0;integer是一个int的封装类,实例化一个内存地址赋值给一个变量,才可以使用,默认值null
- 探探对java多态的理解
多态是面向对象编程思想的三个基本特征之一,指的是一种动态绑定,在执行期间判断引用对象的实际类型,调用其对应的属性或方法。
作用:消除类型之间的耦合关系
- String、StringBuffer、StringBuilder区别
String对象一定被创建,它是不可更改的,编程中看到的同一变量名的String对象,重新赋值的过程实际也是重新创建对象的过程,同时JVM回收了“旧对象”,重复的创建和回收,造成String执行缓慢
StringBuilder和StringBuffer对变量重复赋值或改变保留值,不用重复创建和回收对象,执行速度相比会快一些(除一些特殊情况)
StringBuffer的方法用sychronized
关键字修饰,适合多线程环境下使用,是线程安全的
StringBuilder的方法没有sychronized
关键字修饰,不保证同步操作,适合单线程环境下使用,速度会比StringBuffer快
- 什么是内部类?内部类的作用
内部类,指的是定义在一个类里面的类,分为静态内部类(static修饰的类)、非静态内部类(也叫成员内部类)、方法内部类和匿名内部类,包装内部类的类称为外部类
作用:
内部类提供更好的封装,把内部类隐藏在外部类里面,不允许同一包名的其它类访问该类
保护内部类的隐私数据
- 抽象类和接口区别
抽象类和接口不能直接实例化,抽象类的变量指向实现抽象方法的子类对象,接口的变量指向接口的实现类对象。
抽象类是单一继承,接口可以有多个实现
接口定义的变量只能是公共的静态的常量,抽象类的变量是普通的变量
接口定义的方法只能是公共的抽象的方法,抽象类的方法可以是抽象的方法或非抽象的方法
抽象方法只能被声明,不能实现,接口是设计的结果,抽象类是重构的结果
抽象方法要被实现,所以不能是静态的,也不能是私有的
- 抽象类的意义
抽象类的意义,要依赖抽象,不要依赖具体类,根据多态的特性,在执行期间判断引用对象的类型,调用对应的属性和方法
- 抽象类与接口的应用场景
在一个继承关系的类中,将同一行为的不同表现提取为抽象的方法,将父类声明为抽象类,应用的场景有:BaseActivity、BaseFragment、BaseAdapter、ViewHolder等基类中
接口应用的场景有:BaseAdapter子类监听item单击事件
- 抽象类是否可以没有方法和属性?
抽象类可以没有方法和属性,使用abstract关键字修饰
- 接口的意义
接口的意义,针对接口编程,不针对实现编程,好处是可以有多个实现类,解耦程序,方便修改、维护。
- 泛型中extends和super的区别
泛型类<? extends T>表示以T为上界的一组子类,适合读取数据,不建议调用写入数据的方法,否则编译器会报错;泛型<? super T>表示以T为下界的一组超类,适合调用写入数据的方法,不适合读取数据。
- 父类的静态方法能否被子类重写
父类的静态方法可以被子类继承,但不能被重写(即使子类重写了父类的静态方法,最终调用的仍然是父类的静态方法)
原因:static
修饰的方法,在代码编译时分配了一块内存地址,该内存地址只当程序退出时才会被回收,调用该方法的指针都指向该地址
- 进程和线程的区别
进程是分配资源(CPU、内存)的最小单位,线程是程序执行的最小单位
一个进程可以包含多个线程,线程共享整个进程中的资源(CPU、内存),一个进程至少包含一个线程
线程执行开销小,但不利于资源的管理和保护;而进程正相反
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据;进程间的通信要以通信的方式(IPC)进行
- 序列化的方式
常用的序列化的方式有:实现Serializable接口、实现Externalizable接口、实现Parcelable接口
另外两种不常用的方式:gson、Google protobuf
- Serializable 和Parcelable 的区别
Serializable是Java提供的序列化的方式,使用起来简单但是开销大,序列化和反序列化需要进行大量的I/O操作;
Parcelable是android API增加的序列化的方式,缺点是使用起来稍微麻烦点,但是效率较高。
- 静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?
静态属性和静态方法可以被子类继承,但不可以被重写(即使被子类重写,最终调用的仍然是父类的静态属性和静态方法)
原因:static
修饰的属性和方法,在代码编译期就分配了独立的内存地址,即使子类重写了父类的静态方法,变量指向的还是编译器已分配的内存地址。
- 静态内部类的设计意图
静态内部类实现的效果和独立声明一个类的效果一样,不同之处是静态内部类封装在一个类里
将只想给外部类使用的类声明成静态内部类,可以实现更好的隐藏,不允许除外部类以外的类使用
同时,静态内部类可以通过外部类名区别,可以在不同的类中声明名字一样的静态内部类,比如:Builder
- 成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用
成员内部类,作为外部类的一个成员,可以直接访问外部类的所有属性和方法(包括private属性和方法),成员内部类不允许声明static的变量和方法
应用:一个Activity中,可以声明一个ViewPager的成员内部类,直接使用外部类的数据集
静态内部类,没持有一个指向外部类的引用,不可以使用外部类非static的属性和方法
应用:在使用Builder设计模式的时候,经常需要将内部类设计成static
局部内部类,嵌套在方法作用域内或代码块内,只对该方法的局部变量是可见的
应用:在一个方法里,需要进行复杂的处理过程,将处理的过程使用一个局部内部类封装
匿名内部类,没有访问修饰符,new关键字实现一个接口或继承一个父类(抽象类),重写接口或父类里面的抽象方法,匿名内部类使用一次就被GC回收了
应用:在BaseAdapter的getView方法中,给每一个item添加一个点击的事件
- 谈谈对kotlin的理解
kotlin作为一门新生代的语言,成为当前微信公众号、社区讨论最热门的开发语言之一,有望成为代替Java的Android开发语言。
原因有:
Google主推kotlin作为Android开发的一级语言
IDE支持可以将Java代码一键转成kotlin代码,对Java完美兼容
kotlin编码的大幅度精简、封装大量的函数库、各种语法糖,有效提高了Android的开发效率
- 闭包和局部内部类的区别
可以把闭包理解成编程的一种方式,把“包装”在一个类中的类或“包装”在一个方法中的类称为一个闭包
局部内部类是“包装”在一个方法中的类,可以说局部内部类是一个闭包,它的作用是可以在闭包作用域外访问闭包内的方法和属性
按照上述的定义,成员内部类也可以称为一个闭包
public interface IEngineer {
public void work();
}
public abstract class People {
public abstract void work();
public abstract IEngineer getEngineer();
}
public class Teacher extends People {
IEngineer engineer;
@Override
public void work() {
System.out.println("I am a teachcer !");
}
@Override
public IEngineer getEngineer() {
return engineer;
}
public void setEngineer(IEngineer engineer) {
this.engineer = engineer;
}
}
public static void main(String[] args) {
Teacher teachcer = new Teacher();
teachcer.setEngineer(new IEngineer() {
@Override
public void work() {
System.out.println("I am a English Teacher");
}
});
teachcer.work();
//如何访问一个局部内部类的work()方法?
teachcer.getEngineer().work();
}
- String 转换成 Integer的方式及原理
第一种方式:构造函数
Integer integer=new Integer(str);
System.out.println(integer);
第二种方式:parseInt(String,int)
//parseInt(String,int)将一个字符串转换成基本数据类型int
int value=Integer.parseInt(str);
Integer integer=new Integer(value);//装箱操作
System.out.println(integer);
第三种方式:valueOf(String,int)
//valueOf(String,int)将一个字符串转换成对象Integer
Integer integer=Integer.valueOf(str);
System.out.println(integer);
原理分析:
查看Integer源码,发现将String转换成Integer的过程,先将String转换成基本数据类型int,再通过Integer的装箱操作将int转化成对象,所以上述三种方式最终调用下面两个方法:
public Integer(int value) {
this.value = value;
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
将String转换成基本数据类型源码,逐个识别每个字符
public static Integer valueOf(String s, int radix) throws NumberFormatException {
return Integer.valueOf(parseInt(s,radix));
}
public static int parseInt(String s, int radix)throws NumberFormatException
{
...
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;
}
参考资料:《最全的BAT大厂面试题整理》
你可能感兴趣的文章
转载请注明出处: https://www.teachcourse.cn/2602.html ,谢谢支持!