Android工程师面试准备(一)第一部分

24 篇文章 0 订阅

一、Java面试题

(一) java基础面试知识点

1、java中==和equals和hashCode的区别

“==”:

  ==是运算符,用来比较两个值、两个对象的内存地址是否相等;

“equals()”:

  equals是Object类的方法,默认情况下比较两个对象是否是同一个对象,内部实现是通过“==”来实现的。

如果想比较两个对象的其他内容,则可以通过重写equals方法,

例如:String类就重写了equals方法,改成了对象的内容是否相等。

具体实现可以参考:https://www.cnblogs.com/hujinshui/p/10038044.html

“hashCode()”:

  hashCoed也是Object类里面的方法,返回值是一个对象的哈希码,同一个对象哈希码一定相等,但不同对象哈希码也有可能相等。

如果两个对象通过equals方法比较相等,那么他的hashCode一定相等;

如果两个对象通过equals方法比较不相等,那么他的hashCode有可能相等;

例如以下程序:

class Untitled { public static void main(String[] args) { System.out.println("AaAa".hashCode());//2031744 System.out.println("BBBB".hashCode());//2031744 } }

2、int、char、long各占多少字节数

Java基本类型占用的字节数:

1字节: byte , boolean

2字节: short , char

4字节: int , float

8字节: long , double

编码与中文:

Unicode/GBK: 中文2字节

UTF-8: 中文通常3字节,在拓展B区之后的是4字节

综上,中文字符在编码中占用的字节数一般是2-4个字节。

3、int与integer的区别

  • Integer是int的包装类,int则是java的一种基本数据类型 
  • Integer变量必须实例化后才能使用,而int变量不需要 
  • Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 
  • Integer的默认值是null,int的默认值是0

4、探探对java多态的理解

  • 面向对象的三大基本特征:封装、继承、多态
  • 多态是指:父类引用指向子类对象,在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
  • 实现多态的三要素:继承 重写 父类引用指向子类对象

5、String、StringBuffer、StringBuilder区别

String:

String类是不可变的,所谓不可变意思就是创建一个类后任何对String的改变都 会引发新的String对象的生成;让我们举个例子来看看就明白了:

String str ="abc";

str = str +"def";//这一步jvm会再次创建一个String对象

第二次其实jvm又生成了一个String类,而不是直接覆盖原来的"abc",因此我们说String类是不可改变类。这一种特性会带来一个问题,每次拼接都要创建都要创建一次对象,当我们要拼接大量字符串的时候,效率会变得非常非常慢。

StringBuffer:

StringBuffer不同于String的是StringBuffer是可变的,一样的我们来举个例子看看:

StringBuffer sb =new StringBuffer("abc");

sb.append("efg");//并没有创建一个新的对象

这里第二步并没有产生一个新的对象,而是在原来的基础上追加字符串,这种方式在拼接字符串的时候效率肯定比String要高得多。

StringBuilder:

StringBuffer和StringBuilder类的区别也是如此,他们的原理和操作基本相同,区别在于StringBuffer支持并发操作,线性安全的,适 合多线程中使用。StringBuilder不支持并发操作,线性不安全的,不适合多线程中使用。新引入的StringBuilder类不是线程安全的,但其在单线程中的性能比StringBuffer高。

6、什么是内部类?内部类的作用

什么是内部类:

将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。

内部类的作用:

  • 成员内部类

成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静 态成员)。

当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。

  • 局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

  • 匿名内部类

匿名内部类就是没有名字的内部类

  • 静态内部类

指被声明为static的内部类,他可以不依赖内部类而实例,而通常的内部类需要实例化外部类,从而实例化。静态内部类不可以有与外部类有相同的类名。不能访问外部类的普通成员变量,但是可以访问静态成员变量和静态方法(包括私有类型)

一个 静态内部类去掉static 就是成员内部类,他可以自由的引用外部类的属性和方法,无论是静态还是非静态。但是不可以有静态属性和方法、

作用

  • 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整   
  • 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。   
  • 方便编写事件驱动程序   
  • 方便编写线程代码

7、抽象类和接口区别

抽象类:

  • 抽象类使用abstract修饰;
  • 抽象类不能实例化,即不能使用new关键字来实例化对象;
  • 含有抽象方法(使用abstract关键字修饰的方法)的类是抽象类,必须使用abstract关键字修饰;
  • 抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法;
  • 如果一个子类实现了父类(抽象类)的所有抽象方法,那么该子类可以不必是抽象类,否则就是抽象类;
  • 抽象类中的抽象方法只有方法体,没有具体实现;

接口:

  • 接口使用interface修饰;
  • 接口不能被实例化;
  • 一个类只能继承一个类,但是可以实现多个接口;
  • 接口中方法均为抽象方法;
  • 接口中不能包含实例域或静态方法(静态方法必须实现,接口中方法是抽象方法,不能实现);

8、抽象类的意义

一个类中如果包含抽象方法,这个类应该用abstract关键字声明为抽象类。

  • 为子类提供一个公共的类型;
  • 封装子类中重复内容(成员变量和方法);
  • 定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的。

9、抽象类与接口的应用场景

接口(interface)的应用场合:

  • 类与类之前需要特定的接口进行协调,而不在乎其如何实现。
  • 作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。
  • 需要将一组类视为单一的类,而调用者只通过接口来与这组类发生联系。
  • 需要实现特定的多项功能,而这些功能之间可能完全没有任何联系。

抽象类(abstract class)的应用场合:

一句话,在既需要统一的接口,又需要实例变量或缺省的方法的情况下,就可以使用它。最常见的有:

  • 定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口。可以用abstract class定义一组方法体,甚至可以是空方法体,然后由子类选择自己所感兴趣的方法来覆盖。
  • 某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还必需类中表示状态的变量来区别不同的关系。abstract的中介作用可以很好地满足这一点。
  • 规范了一组相互协调的方法,其中一些方法是共同的,与状态无关的,可以共享的,无需子类分别实现;而另一些方法却需要各个子类根据自己特定的状态来实现特定的功能

10、抽象类是否可以没有方法和属性?

答案是肯定的,可以。

    抽象类中可以没有抽象方法,但有抽象方法的一定是抽象类。所以,java中 抽象类里面可以没有抽象方法。注意即使是没有抽象方法和属性的抽象类,也不能被实例化。

11、接口的意义

  • 定义接口的重要性:在Java编程,abstract class 和interface是支持抽象类定义的两种机制。正是由于这两种机制的存在,才使得Java成为面向对象的编程语言。
  • 定义接口有利于代码的规范:对于一个大型项目而言,架构师往往会对一些主要的接口来进行定义,或者清理一些没有必要的接口。这样做的目的一方面是为了给开发人员一个清晰的指示,告诉他们哪些业务需要实现;同时也能防止由于开发人员随意命名而导致的命名不清晰和代码混乱,影响开发效率。
  • 有利于对代码进行维护:比如你要做一个画板程序,其中里面有一个面板类,主要负责绘画功能,然后你就这样定义了这个类。可是在不久将来,你突然发现现有的类已经不能够满足需要,然后你又要重新设计这个类,更糟糕是你可能要放弃这个类,那么其他地方可能有引用他,这样修改起来很麻烦。如果你一开始定义一个接口,把绘制功能放在接口里,然后定义类时实现这个接口,然后你只要用这个接口去引用实现它的类就行了,以后要换的话只不过是引用另一个类而已,这样就达到维护、拓展的方便性。
  • 保证代码的安全和严密:一个好的程序一定符合高内聚低耦合的特征,那么实现低耦合,定义接口是一个很好的方法,能够让系统的功能较好地实现,而不涉及任何具体的实现细节。这样就比较安全、严密一些,这一思想一般在软件开发中较为常见。

12、泛型中extends和super的区别

extends:

泛型中extends的主要作用是设定类型通配符的上限

要理解这句话,我们先从一个例子来看:

class Fruit{ public void call() { System.out.println("这是一个水果"); } } class Banana extends Fruit{ @Override public void call() { System.out.println("这是一个香蕉"); } } class Apple extends Fruit{ @Override public void call() { System.out.println("这是一个苹果"); } } public class Test{ public void test1(List<Fruit> fruits) { for (Fruit fruit: fruits) { fruit.call(); } } public static void main(String[] args) { List<Apple> apples = new ArrayList<>(); List<Fruit> fruits = apples; //类型转换失败 Test test = new Test(); test.test1(fruits); //失败 } }

我们使用Apple继承了Fruit类,然后建立了两个list,一个容纳的是apple,一个容纳的是fruit。按照常理来说,因为Apple继承了Fruit,List<Apple>应该也是List<Fruit>的子类型。但是实际上不是这样的,运行上述程序,会报一个如下所示的错误。

Error:(42, 20) java: 不兼容的类型: java.util.List<com.company.Apple>无法转换为java.util.List<com.company.Fruit>

我们可以这样理解,如果上述代码能够正常运行,那把call方法修改成添加一个Banana对象会怎么样,因为test1方法中实际上使用的是List<Apple>,是不能够添加到Banana的,就会出错。所以List<Apple>不是List<Fruit>的子类型。

那我们如何处理类似的情况呢,这就需要使用extends了。、

 

public class Test{

public void test1(List<? extends Fruit> fruits) {

for (Fruit fruit: fruits) {

fruit.call(); } }

public static void main(String[] args) {

List<Apple> apples = new ArrayList<>();

Test test = new Test();

test.test1(apples);

}

}

<? extends Fruit>代表的是上界通配符,也就是说这个List中存放的对象都是Fruit以及其子类的对象,这样我们就不用因为输入的List中类型的不同而改变代码了。

上界通配符有一个特点,就是程序只知道List<? extends Fruit>中的对象是Fruit的子类的对象,但是如果Fruit的子类有很多个,那个在使用add方法的时候,就可能出现本来是List<Apple>,然后在其中添加了banana对象,从而失败。

super:

super与extends是完全相反的,其定义的是下界通配符。

List<? super Fruit>也就是说List中存放的都是Fruit和它的父类的对象,比如food,Object。而且如果要在这个List中取出数据,那就不能够确定具体是Fruit的哪个父类的对象,可能是Food,可能是Object。为了保证一定能够取出来,就必须把其转型成Object对象,但是这个时候就会失去原有对象的类型信息。所以List<? super Fruit>不能够提取数据。

13、父类的静态方法能否被子类重写

首先答案是不能!

这个问题有两个关键字,一个是静态方法,一个是重写。

我们来先说说重写,可能很多初学的朋友分不清重写和重载的区别。

重写:子类继承父类后,定义了一个和父类中的一模一样方法,这个一模一样是值方法名和参数的定义一模一样。这时候子类要实现这个方法,就称为对父类方法的重写。

重载:子类继承父类后,定义了一个和父类中相同名字的方法,但是参数不一样(必须),实现也不同(可选),这就是重载。

静态方法:java中,static修饰符修饰的方法就是静态方法。所谓静态就是指:在编译之后所分配的内存会一直存在(不会被回收),直到程序退出内存才会释放这个空间。

在java中,所有的东西都是对象,对象的抽象就是类,对于一个类而言,如果要使用他的成员(类中的属性,方法等),一般情况下,必须先实例化对象后,通过对象的引用才能访问这些成员。

但是,如果要使用的成员使用了static修饰,就可以不通过实例化获得该成员。

就比如,现在有个桌子,我想吃苹果,一般情况下,我需要先拿个盘子(对象的实例化)去装苹果才能吃到苹果,现在有个苹果直接放在桌子上(用static修饰过的静态方法),这样我就可以直接从桌子上拿到苹果。可能大家会有疑问,既然静态方法(能不通过实例化就使用成员)这么方便,为什么不都使用静态方法。

大家回到static这个修饰符的功能定义:所谓静态就是指:在编译之后所分配的内存会一直存在(不会被回收),直到程序退出内存才会释放这个空间。

java的回收机制会定时的回收已经使用过的对象的内存来释放内存给应用程序。如果全部都是静态变量和静态方法,内存都被占用了,java程序哪里还有运行的空间呢?就好比,你回家就看到桌子上摆满了苹果,那你买的梨子就没地方放了。

明出处。

现在回到题目中来,父类的静态方法能不能被重写。答案是不能。

因为静态方法从程序开始运行后就已经分配了内存,也就是说已经写死了。所有引用到该方法的对象(父类的对象也好子类的对象也好)所指向的都是同一块内存中的数据,也就是该静态方法。子类中如果定义了相同名称的静态方法,并不会重写,而应该是在内存中又分配了一块给子类的静态方法,没有重写这一说。

14、进程和线程的区别

多进程:

进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。

Linux系统函数fork()可以在父进程中创建一个子进程,这样的话,在一个进程接到来自客户端新的请求时就可以复制出一个子进程让其来处理,父进程只需负责监控请求的到来,然后创建子进程让其去处理,这样就能做到并发处理。

多线程:

线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。

线程和进程各自有什么区别和优劣呢?

  • 进程是资源分配的最小单位,线程是程序执行的最小单位。
  • 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
  • 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
  • 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

15、final,finally,finalize的区别

  • final可以用于修饰变量,方法,类,被修饰的变量的值不能被改变,被修饰的方法不能被重写,被修饰的类不能被继承,它和abstract是反义词
  • finally通常放在try…catch…的后面构造总是执行代码块,这就意味着程序无论正常运行还是发生异常,这里的代码只要JVM不关闭都会执行,可以将释放外部资源的代码写在finally块中
  • finalize:Object类的方法,Java中允许使用finalize()方法在垃圾回收器将对象从内存中清除出去之前做必要的清理工作,这个方法是由垃圾回收器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作

16、序列化的方式

  • implements Parcelable
  • implements Serializable

17、Serializable 和Parcelable 的区别

  • 在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
  • Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
  • Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点, 也不提倡用,但在这种情况下,还是建议你用Serializable 。

18、静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?

父类的静态属性和方法可以被子类继承;不可以被子类重写。

原因

static修饰函数/变量时,其实是全局函数/变量,它只是因为java强调对象的要

挂,它与任何类都没有关系。靠这个类的好处就是这个类的成员函数调用static方法不用带类名。

注意:static关键字可以用修饰代码块.static代码块可以置于类中的任何一个位置,并可以有多个static代码块。在类初次被加载时,会按照静态代码块的顺序来执行,并且只会执行一次。

19、静态内部类的设计意图

态内部类与非静态内部类之间存在一个最大的区别:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。

  • 它的创建是不需要依赖于外围类的。
  • 它不能使用任何外围类的非static成员变量和方法。

20、成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用

一、成员内部类

定义在类内部的非静态类,就是成员内部类。

 

public class Out {

private static int a;

private int b;

public class Inner {

public void print() {

System.out.println(a);

System.out.println(b);

}

}

}

二、静态内部类:

定义在类内部的静态类,就是静态内部类。

 

public class Out {

private static int a;

private int b;

public static class Inner {

public void print() {

System.out.println(a);

}

}

}

应用场景:

Java集合类HashMap内部就有一个静态内部类Entry。Entry是HashMap存放元素的抽象,HashMap内部维护Entry数组用了存放元素,但是Entry对使用者是透明的。像这种和外部类关系密切的,且不依赖外部类实例的,都可以使用静态内部类。

三、局部内部类

定义在方法中的类,就是局部类。(局部内部类是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。)

public class Out {

private static int a;

private int b;

public void test(final int c) {

final int d = 1;

class Inner {

public void print() {

System.out.println(a);

System.out.println(b);

System.out.println(c);

System.out.println(d);

}

}

}

public static void testStatic(final int c) {

final int d = 1;

class Inner {

public void print() {

System.out.println(a);

//定义在静态方法中的局部类不可以访问外部类的实例变量

//System.out.println(b);

System.out.println(c);

System.out.println(d);

}

}

}

}

应用场景:

如果一个类只在某个方法中使用,则可以考虑使用局部类。

四、匿名内部类

匿名内部类是没有访问修饰符的。

  1. new 匿名内部类,这个类首先是要存在的。
  2. 当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。
  3. 匿名内部类没有明面上的构造方法,编译器会自动生成一个引用外部类的构造方法。

 

public class Out {

private static int a;

private int b;

private Object obj = new Object() {

private String name = "匿名内部类";

@Override

public String toString() {

return name;

}

};

public void test() {

Object obj = new Object() {

@Override

public String toString() {

System.out.println(b);

return String.valueOf(a);

}

};

System.out.println(obj.toString());

}

}

应用场景:

匿名内部类使用广泛,比如我们常用的绑定监听的时候

 

view.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Toast.makeText(v.getContext(),"click",Toast.LENGTH_SHORT).show(); }

});

21、闭包和局部内部类的区别

  • 局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
  • 闭包(Closure)是一种能被调用的对象,它保存了创建它的作用域的信息。JAVA并不能显式地支持闭包,但是在JAVA中,闭包可以通过“接口+内部类”来实现。

例如:一个接口程序员和一个基类作家都有一个相同的方法work,相同的方法名,但是其含义完全不同,这时候就需要闭包。

class Food{

public static final String name = "Food";

private static int num = 20;

public Food() {

System.out.println("Delicious Food");

}

public Active getEat() {

return new EatActive();

}

private class EatActive implements Active {

@Override

public void eat() {

if (num == 0) {

System.out.println("吃货,已经吃没了");

}

num --;

System.out.println("吃货,你吃了一份了");

}

}

public void currentNum() {

System.out.println("还剩:"+num+"份");

}

}

interface Active{

void eat();

}

22、string 转换成 integer的方式及原理

  1. parseInt(String s)--内部调用parseInt(s,10)(默认为10进制)
  2. 正常判断null,进制范围,length等
  3. 判断第一个字符是否是符号位
  4. 循环遍历确定每个字符的十进制值
  5. 通过*= 和-= 进行计算拼接
  6. 判断是否为负值 返回结果。

 

(二) java深入源码级的面试题

1、哪些情况下的对象会被垃圾回收机制处理掉?

  • 没有引用指向
  • 只有弱引用指向并且不回收弱引用对象的话存储区无空间
  • 虚引用指向的对象

满足以上任意条件则在gc时会回收

2、讲一下常见编码方式?

什么是编码:

编码是从一种形式或格式转换为另一种形式的过程也称为计算机编程语言的代码简称编码。计算机中存储信息的最小单元是一个字节,即8个bit。

常见的编码方式:

ASCII码:共有128个,用一个字节的低7位表示 ISO8859-1:在ASCII码的基础上涵盖了大多数西欧语言字符,仍然是单字节编码,它总共能表示256个字符

GB2312:全称为《信息交换用汉字编码字符集基本集》,它是双字节编码,总的编码范围是A1~F7 A1~A9 ·符号区 B0~F7 汉字区

GBK:数字交换用汉字编码字符集》,它可能是单字节、双字节或者四字节编码,与GB2312编码兼容

UTF-16:具体定义了Unicode字符在计算机中的存取方法。采用2字节来表示Unicode转化格式,它是定长的表示方法,不论什么字符都可以用两个字节表示

UTF-8: UTF-8采用一种变长技术,每个编码区域有不同的字码长度,不同的字符可以由1~6个字节组成。 如果一个字节,最高位为0,表示这是一个ASCII字符(00~7F) 如果一个字节,以11开头,连续的1的个数暗示这个字符的字节数

一个utf8数字占1个字节

一个utf8英文字母占1个字节

少数是汉字每个占用3个字节,多数占用4个字节。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值