一般来说,这三个东西基本在面试中70%会被问到,而问的方向也不太一样。比如初级的问法是讲讲它们之前的区别,这个我想没什么难度,大多数人还是知道主要核心区别是并发上的处理。此外,内部数据结构的实现、扩容、存取操作这些问题应该是很老生常谈了,这并没有什么好说的,大多数人也都知道。稍微问的深一点的可能会在下面这些点上出问题。哈希碰撞,哈希计算,哈希映射,为什么是头插法,扩容为什么是2的幂次等这样的问题。
这个问题被问频率不在HashMap之下,因为并发编程,真的很重要。能问到这几个点的方式真的是太多了,我们能发挥的空间也同样很大。CAS的ABA问题?上面几个东西的特性?使用场景?大概我不用再例举了吧?对了,我多次被问到的一个问题是:synchronized修饰实例方法和修饰静态方法有啥不一样。
这三个问题大概出现概率40%,基本只需要看我每日一问系列的推文就差不多了吧,希望更清楚明白的可以直接看《深入理解Java虚拟机》。当你讲到分代回收算法的时候,不免会被追问到新生对象是怎么从年轻代到老年代的,以及可以作为root结点的对象有哪些两个问题。
四大引用面试出现概率比我想象中要高,我原本以为就强引用、软引用、弱引用、虚引用这四个玩意儿没啥可讲的。实际上也确实没啥好讲的,稍微问的深一些的面试官会和内存泄漏检测原理以及垃圾回收糅杂在一起。
Java泛型还是会在面试中出现的,不过几率不是很高,大概是因为我简历中有提到泛型擦除相关的东西。所以会被问到泛型、泛型擦除、通配符相关的东西。不过这个东西,不应该是为了应付面试,实际开发中真的很重要。
老生常谈的问题,没啥好说的,实际上这次社招面试也只遇到了两次。比较喜欢追根溯源的面试官可能会对这个finalize有点执念,一定希望搞清楚,这玩意儿我们是不是可以真的搞点黑科技骚操作。
大多数Android应用开发并接触不到很多并发相关的东西,不过这玩意儿还是在面试中挺容易出现的。
10.java中==和equals和hashCode的区别
1)若是基本数据类型比较,是比较值,若是引用类型,则比较的是他们在内存中的存放地址。对象是存放在堆中,栈中存放的对象的引用,所以是对栈中的值进行比较,若返回true代表变量的内存地址相等;
2)equals是Object类中的方法,Object类的equals方法用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象)。若是类中覆盖了equals方法,就要根据具体代码来确定,一般覆盖后都是通过对象的内容是否相等来判断对象是否相等。
3)hashCode()计算出对象实例的哈希码,在对象进行散列时作为key存入。之所以有hashCode方法,因为在批量的对象比较中,hashCode比较要比equals快。在添加新元素时,先调用这个元素的hashCode方法,一下子能定位到它应该旋转的物理位置,若该位置没有元素,可直接存储;若该位置有元素,就调用它的equals方法与新元素进行比较,若相同则不存,不相同,就放到该位置的链表末端。
4)equals与hashCode方法关系:hashCode()是一个本地方法,实现是根据本地机器上关的。equals()相等的对象,hashCode()也一定相等;hashCode()不等,equals()一定也不等;hashCode()相等,equals()可能相等,也可能不等。所以在重写equals(Objectobj)方法,有必要重写hashCode()方法,确保通过equals(Objectobj)方法判断结果为true的两个对象具备相等的hashCode()返回值。
5)equals与的关系:Integerb1=127;在java编译时被编译成Integerb1=Integer.valueOf(127);对于-128到127之间的Integer值,用的是原生数据类型int,会在内存里供重用,也就是这之间的Integer值进行比较时,只是进行int原生数据类型的数值进行比较。而超出-128〜127的范围,进行==比较时是进行地址及数值比较。
都是字符串类,String类中使用字符数组保存字符串,因有final修饰符,String对象是不可变的,每次对String操作都会生成新的String对象,这样效率低,且浪费内存空间。但线程安全。
StringBuilder和StringBuffer也是使用字符数组保存字符,但这两种对象都是可变的,即对字符串进行append操作,不会产生新的对象。它们的区别是:StringBuffer对方法加了同步锁,是线程安全的,StringBuilder非线程安全。
编码的意义:计算机中存储的最小单元是一个字节即8bit,所能表示的字符范围是255个,而人类要表示的符号太多,无法用一个字节来完全表示,固需要将符号编码,将各种语言翻译成计算机能懂的语言。
1.parseInt(Strings)内部调用parseInt(s,10)默认为10进制。
2.正常判断null\进制范围,length等。
3.判断第一个字符是否是符号位。
4.循环遍历确定每个字符的十进制值。
5.通过*=和-=进行计算拼接。
6.判断是否为负值返回结果。
代理是一种常用的设计模式,目的是:为其他对象提供一个代理以控制对某个对象的访问,将两个类的关系解耦。代理类和委托类都要实现相同的接口,因为代理真正调用的是委托类的方法。
区别:
1)静态代理:由程序员创建或是由特定工具生成,在代码编译时就确定了被代理的类是哪一个是静态代理。静态代理通常只代理一个类;
2)动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口下的多个实现类;
实现步骤:a.实现InvocationHandler接口创建自己的调用处理器;b.给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类;c.利用反射机制得到动态代理类的构造函数;d.利用动态代理类的构造函数创建动态代理类对象;
使用场景:Retrofit中直接调用接口的方法;Spring的AOP机制
在运行状态中,对任意一个类,都能知道这个类的所有属性和方法,对任意一个对象,都能调用它的任意一个方法和属性。这种能动态获取信息及动态调用对象方法的功能称为java语言的反射机制。
反射的作用:开发过程中,经常会遇到某个类的某个成员变量、方法或属性是私有的,或只对系统应用开放,这里就可以利用java的反射机制通过反射来获取所需的私有成员或是方法。
1)获取类的Class对象实例Classclz=Class.forName("com.zhenai.api.Apple");
2)根据Class对象实例获取Constructor对象ConstructorappConstructor=clz.getConstructor();
3)使用Constructor对象的newInstance方法获取反射类对象ObjectappleObj=appConstructor.newInstance();
4)获取方法的Method对象MethodsetPriceMethod=clz.getMethod("setPrice",int.class);
5)利用invoke方法调用方法setPriceMethod.invoke(appleObj,14);
6)通过getFields()可以获取Class类的属性,但无法获取私有属性,而getDeclaredFields()可以获取到包括私有属性在内的所有属性。带有Declared修饰的方法可以反射到私有的方法,没有Declared修饰的只能用来反射公有的方法,其他如Annotation\Field\Constructor也是如此。
内存区域划分:
1.Java规定所有变量的内存都需要存储在主内存。
2.每个线程都有自己的工作内存,线程中使用的所有变量以及对变量的操作都基于工作内存,工作内存中的所有变量都从主内存读取过来的。
3.不同线程间的工作内存无法进行直接交流,必须通过主内存完成。
主内存和工作内存之间的交互协议,即变量如何从主内存传递到工作内存、工作内存如何将变量传递到主内存,Java内存模型定义了8种操作来完成,并且每一种操作都是原子的,不可再分的。
1.指向方法区:"abc"是常量,所以它会在方法区中分配内存,如果方法区已经给"abc"分配过内存,则s1会直接指向这块内存区域。
2.指向Java堆:newString("abc")是重新生成了一个Java实例,它会在Java堆中分配一块内存。
所以s1和s2的内存地址肯定不一样,但是内容一样。
判断一个对象可以回收通常采用的算法是引用几算法和可达性算法。由于互相引用导致的计数不好判断,Java采用的可达性算法。
可达性算法的思路是:通过一些列被成为GCRoots的对象作为起始点,自上往下从这些起点往下搜索,搜索所有走过的路径称为引用链,如果一个对象没有跟任何引用链相关联的时候,则证明该对象不可用,所以这些对象就会被判定为可以回收。
可以被当作GCRoots的对象包括:
类加载的过程可以分为:
1.加载:将类的全限定名转化为二进制流,再将二进制流转化为方法区中的类型信息,从而生成一个Class对象。
2.验证:对类的验证,包括格式、字节码、属性等。
3.准备:为类变量分配内存并设置初始值。
4.解析:将常量池的符号引用转化为直接引用。
5.初始化:执行类中定义的Java程序代码,包括类变量的赋值动作和构造函数的赋值。
6.使用
7.卸载
只有加载、验证、准备、初始化和卸载的这个五个阶段的顺序是确定的。
类加载的机制是双亲委派模型。大部分Java程序需要使用的类加载器包括:
双亲委派模型如下:
双亲委派模型要求出了顶层的启动类加载器之外,其他的类加载器都有自己的父加载器,通过组合实现。
双亲委派模型的工作流程:
当一个类加载的任务来临的时候,先交给父类加载器完成,父类加载器交给父父类加载器完成,知道传递给启动类加载器,如果完成不了的情况下,再依次往下传递类加载的任务。
这样设计的原因:
双亲委派模型能够保证Java程序的稳定运行,不同层次的类加载器具有不同优先级,所有的对象的父类Object,无论哪一个类加载器加载,最后都会交给启动类加载器,保证安全。
答:在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:ApplicationNotResponding)对话框。用户可以选择让程序继续运行,但是,他们在使用你的应用程序时,并不希望每次都要处理这个对话框。因此,在程序里对响应性能的设计很重要这样,这样系统就不会显示ANR给用户。
不同的组件发生ANR的时间不一样,Activity是5秒,BroadCastReceiver是10秒,Service是20秒(均为前台)。
如果开发机器上出现问题,我们可以通过查看/data/anr/traces.txt即可,最新的ANR信息在最开始部分。
以上内容全都已打包整理好,可以点击这里直达免费获取!!!