2023年6月20日发(作者:)

Android⾼级⼯程师⾯试题整理——java⾯试题 相信每次⾯试之前,⼤家都会⼤量刷⼀下⾯试题来应该对各种公司的⾯试吧,下⾯整理⼀下android⽅⾯的⾯试题分享给⼤家。本⽂主要分为以下⼏部分:Android⾯试题⾼级开发技术⾯试题跨平台Hybrid 开发1、Java中、hascode和==的区别  ==(双等号):对于基本数据类型(byte、short、char、int、long、float、double、boolean)来说,⽐较的是他们的值;对于引⽤类型(各种new出来的对象)来说⽐较的是他们的地址。  equals:默认情况(没有覆盖equals⽅法)下equals⽅法都是调⽤Object类的equals⽅法,⽽Object的equals⽅法是判断对象的内存地址。⼀般地都是覆盖这个⽅法,根据对象的内容是否相等来判断对象是否相等。如:String类就是覆盖这个⽅法,先判断对象的内存地址是否相等,之后再判断内容是否相等。  hascode:hascode()⽅法返回的就是⼀个数值,其⽬的是⽣成⼀个hash码,hash码的主要⽤途就是在对对象进⾏散列的时候作为key输⼊,我们需要每个对象的hash码尽可能不同,这样才能保证散列的存取性能。  在java中⼀般的equals()和hascode()通常要遵从如下⼏点约定,这样才能保证这两个⽅法在java中的⼀个规范,这也是为什么重写equals()⽅法需要重写hascode()⽅法的原因:1. 如果两个对象equals,java运⾏时环境会认为他们的hashcode⼀定相等;2. 如果两个对象不equals,他们的hashcode有可能相等。3. 如果两个对象hashcode相等,他们不⼀定hashcode。4. 如果两个对象hashcode不相等,他们⼀定不equals。2、int、char、long各占多少字节数  int占4个字节、char占2个字节、long占8个字节。3、int与integer的区别  int是基本数据类型,直接存数据。⽽integer是对象,⽤⼀个引⽤指向这个对象,是int的封装类。4、谈谈对java多态的理解  多态是指:⽗类引⽤指向⼦类对象,在执⾏期间会根据引⽤对象的实际类型,调⽤具体对象的相应⽅法。  ⼀般可以通过两种⽅式实现多态:重写、重载。  实现多态的三要素:集成、重写、⽗类引⽤指向⼦类对象。5、String、StringBuffer、StringBuilder区别 String:1. 是字符串常量,⼀旦创建就不能修改。对于已经存在了的String对象的修改都是重新创建⼀个新的对象,然后把新的值保存进去。2. String是final类,不能被继承。3. String覆盖了equals⽅法和hashCode⽅法。 StringBuffer:1. 是字符串可变对象,可以对字符串进⾏操作,修改字符串是不会新建⼀个对象。2. 执⾏效率较慢,但是线程安全。3. StringBuffer没有覆盖了equals⽅法和hashCode⽅法。 StringBuilder:1. 也是字符串可变对象,同StringBuffer⼀样,可以对字符串进⾏操作,也 不会新建对象。2. 执⾏效率⾼,但是线程不安全。6、什么是内部类?内部类的作⽤ 什么是内部类:   将⼀个类定义在另⼀个类⾥⾯或者⼀个⽅法⾥⾯,这样的类称为内部类。 作⽤:1. 解决多继承问题。在java中⼀个类只能单继承,⽽内类提供了⼀种多继承的解决⽅案。2. ⽅便将存在⼀定逻辑关系的组织在⼀起,⼜可以对外界隐藏。3. ⽅便编写事件驱动程序。4. ⽅便编写线程代码。7、抽象类和接⼝的区别1. 抽象类要被类继承,接⼝要被类实现;2. 接⼝只能做⽅法的声明,抽象类中可以作⽅法的声明,也可以作⽅法的实现;3. 接⼝⾥定义的变量只能是公共的静态常量,抽象类中的变量可以是普通变量;4. 接⼝是设计的结果,抽象类是重构的结果;5. 抽象类和接⼝都是⽤来抽象具体对象的,但是接⼝的抽象级别最⾼;6. 抽象类可以有具体的⽅法和属性,接⼝只能有抽象⽅法和常量;7. 抽象类主要⽤来抽象类别,接⼝主要⽤来抽象功能;8、抽象类的意义1. 为⼦类提供⼀个公共的类型;2. 封装⼦类中重复内容(成员变量和⽅法);3. 定义抽象⽅法,⼦类虽然有不同的实现,但改⽅法的定义是⼀致的。9、抽象类与接⼝的应⽤场景 接⼝的应⽤场景:1. 类与类之间需要特定的接⼝进⾏协调,⽽不在乎如何实现; 2. 作为能够实现特定功能的标识存在,也可以是什么接⼝⽅法都没有的纯粹标识。3. 需要将⼀组类视为单⼀的类,⽽调⽤者只通过接⼝来与这组类发⽣联系;4. 需要实现特定的多项功能,⽽这些功能之间可能完全没有任何联系; 抽象类的应⽤场景:1. 定义了⼀组接⼝,但⼜不想强迫每个实现类都必须实现所有的接⼝,可以⽤abstract定义⼀组⽅法,甚⾄可以是空⽅法体,然后有⼦类选择⾃⼰所感兴趣的⽅法来覆盖;2. 某些场合下,只靠纯粹的接⼝不能满⾜类与类之间的协调,还必须类中表⽰状态的变量来区别不同的关系。Abstract的中介作⽤可以很好地满⾜这⼀点;3. 规范了⼀组相互协调的⽅法,其中⼀些⽅法是共同的,与状态⽆关的,可以共享的,⽆需⼦类分别实现;⽽另⼀些⽅法却需要各个⼦类根据⾃⼰特定的状态来实现特定的功能;10、接⼝的意义1. 重要性:在java中,abstract class 和interface 是⽀持抽象类定义的两种机制,接⼝是特殊的抽象类,在java中⼀个类只能继承⼀个⽗类却可以实现多个接⼝。正是由于这两种机制的存在,才赋予了java强⼤的⾯向对象能⼒。java的特性封装、继承、多态。在java中有两种形式可以实现多态:继承和接⼝;2. 简单、规范性:如果⼀个项⽬⽐较庞⼤,那么就需要⼀个能理清所有业务的架构师来定义⼀些主要的接⼝,这些接⼝不仅告诉开发⼈员你需要实现哪些业务,⽽且也将命名规范限制住了(防⽌⼀些开发⼈员随便命名导致别的程序员⽆法看明⽩);3. 维护、拓展性:由于接⼝只做定义,⽽不关注实现,在某些功能模块中,可以通过接⼝去应⽤实现类,这能很好的降低功能模块之间的耦合性,从⽽修改某些模块的实现,不会影响到其他模块,⽅便后期的维护、拓展。4. 安全、严密性:接⼝是实现软件松耦合的重要⼿段,它描述了系统对外的所有服务,⽽不涉及任何具体的实现细节。这样就⽐较安全、严密⼀些。11、泛型中extends和super的区别  泛型中extends主要作⽤是类型的设定上界通配符,对应的为T及其⼦类对象;  泛型中super与extends是完全相反的,其定义是下界通配符,对应的为T及其⼦类对象;12、⽗类的静态⽅法能否被⼦类重写  不能。⽗类的静态⽅法能够被⼦类继承,但是不能够被⼦类重写,即使⼦类中的静态⽅法与⽗类中的静态⽅法完全⼀样,也是两个完全不同的⽅法。对于静态⽅法和静态变量来说,虽然在代码中可以通过⼦类对象来调⽤,当时在编译的时候就将其与类绑定在⼀起,既然它们在编译的时候就决定了调⽤的⽅法、变量,那就和重写没有关系了。重写指的是根据运⾏时对象的类型来决定调⽤哪个⽅法,⽽不是根据编译时的类型。13、进程和线程的区别进程是资源分配的最⼩单位,线程是资源调度的最⼩单位(程序执⾏的最⼩单位);进程有⾃⼰的独⽴地址空间,每启动⼀个进程,系统就会为它分配地址空间,建⽴数据表来维护代码段、堆栈段和数据段,这种操作⾮常昂贵。⽽线程是共享进程中的数据的,使⽤相同的地址空间,因此CPU切换⼀个线程的花费远⽐进程要⼩很多,同时创建⼀个线程的开销也⽐进程要⼩很多。线程之间的通信更⽅便,同⼀进程下的线程共享全局变量、静态变量等数据,⽽进程之间的通信需要以通信的⽅式(IPC)进⾏。但是多进程程序更健壮,多线程程序只要有⼀个线程死掉,整个进程也死掉了,⽽⼀个进程死掉并不会对另外⼀个进程造成影响,因为进程有⾃⼰独⽴的地址空间。14、final,finally,finalize的区别final:   在java中,final可以⽤来修饰类,⽅法和变量(成员变量或局部变量)。修饰类:当⽤final修饰类的时,表明该类不能被其他类所继承;修饰⽅法:即此⽅法不能被重写;修饰变量:表⽰常量,只能被赋值⼀次,赋值后其值不再改变;finally:   finally作为异常处理的⼀部分,它只能⽤在try/catch语句中,并且附带⼀个语句块,表⽰这段语句最终⼀定会被执⾏(不管有没有抛出异常,哪怕有return语句,都会执⾏),经常被⽤在需要释放资源的情况下。finalize   finalize()是在⾥定义的,也就是说每⼀个对象都有这么个⽅法。这个⽅法在gc启动,该对象被回收的时候被调⽤。其实gc可以回收⼤部分的对象(凡是new出来的对象,gc都能搞定,⼀般情况下我们⼜不会⽤new以外的⽅式去创建对象),所以⼀般是不需要程序员去实现finalize的。   特殊情况下,需要程序员实现finalize,当对象被回收的时候释放⼀些资源,⽐如:⼀个socket链接,在对象初始化时创建,整个⽣命周期内有效,那么就需要实现finalize,关闭这个链接。   ⼀个对象的finalize()⽅法只会被调⽤⼀次,⽽且finalize()被调⽤不意味着gc会⽴即回收该对象,所以有可能调⽤finalize()后,该对象⼜不需要被回收了,然后到了真正要被回收的时候,因为前⾯调⽤过⼀次,所以不会调⽤finalize(),产⽣问题。 所以,推荐不要使⽤finalize()⽅法,它跟析构函数不⼀样。15、堆与栈内存的区别  堆:是⼤家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是⽤户分配的空间。堆在操作系统对进程初始化的时候分配,运⾏过程中也可以向系统要额外的堆,但是记得⽤完了要还给操作系统,要不然就是内存泄漏。   栈:是个线程独有的,保存其运⾏状态和局部⾃动变量的。栈在线程开始的时候初始化,每个线程的栈互相独⽴,因此,栈是 thread safe的。操作系统在切换线程的时候会⾃动的切换栈,就是切换 SS/ESP寄存器。栈空间不需要在⾼级语⾔⾥⾯显式的分配和释放。16、序列化的⽅式1. 实现Serializable接⼝(隐式序列化):通过实现Serializable接⼝,这种是隐式序列化(不需要⼿动),这种是最简单的序列化⽅式,会⾃动序列化所有⾮static和 transient关键字修饰的成员变量。2. 实现Externalizable接⼝(显式序列化):Externalizable接⼝继承⾃Serializable, 我们在实现该接⼝时,必须实现writeExternal()和readExternal()⽅法,⽽且只能通过⼿动进⾏序列化,并且两个⽅法是⾃动调⽤的,因此,这个序列化过程是可控的,可以⾃⼰选择哪些部分序列化。3. 实现Serializable接⼝+添加writeObject()和readObject()⽅法。(显+隐序列化):先实现Serializable接⼝,并且添加writeObject()和readObject()⽅法。注意这⾥是添加,不是重写或者覆盖。但是添加的这两个⽅法必须有相应的格式。⽅法必须要被private修饰; ----->才能被调⽤第⼀⾏调⽤默认的defaultRead/WriteObject(); ----->隐式序列化⾮static和transient调⽤read/writeObject()将获得的值赋给相应的值; —>显式序列化17、Serializable 和Parcelable 的区别1、平台区别Serializable是属于 Java ⾃带的,表⽰⼀个对象可以转换成可存储或者可传输的状态,序列化后的对象可以在⽹络上进⾏传输,也可以存储到本地。Parcelable 是属于 Android 专⽤。不过不同于Serializable,Parcelable实现的原理是将⼀个完整的对象进⾏分解。⽽分解后的每⼀部分都是Intent所⽀持的数据类型。2、编写上的区别Serializable代码量少,写起来⽅便;Parcelable代码多⼀些,略复杂;3、选择的原则如果是仅仅在内存中使⽤,⽐如activity、service之间进⾏对象的传递,强烈推荐使⽤Parcelable,因为Parcelable⽐Serializable性能⾼很多。因为Serializable在序列化的时候会产⽣⼤量的临时变量, 从⽽引起频繁的GC。如果是持久化操作,推荐Serializable,虽然Serializable效率⽐较低,但是还是要选择它,因为在外界有变化的情况下,Parcelable不能很好的保存数据的持续性。4、本质的区别Serializable的本质是使⽤了反射,序列化的过程⽐较慢,这种机制在序列化的时候会创建很多临时的对象,⽐引起频繁的GC;Parcelable⽅式的本质是将⼀个完整的对象进⾏分解,⽽分解后的每⼀部分都是Intent所⽀持的类型,这样就实现了传递对象的功能了。18、静态属性和静态⽅法是否可以被继承?是否可以被重写?以及原因?  可以继承、不能重载;原因:   因为静态属性、⽅法从程序开始运⾏后就已经分配了内存。所有通过对象来访问该属性、⽅法的⽅式,都会在编译的时候就将其与类绑定在⼀起,就决定了调⽤的⽅法、变量,对象⽆关。19、静态内部类的设计意图20、成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项⽬中的应⽤21、谈谈对kotlin的理解22、闭包和局部内部类的区别  闭包:是能获取其他函数内部变量的函数。是⼀种能被调⽤的对象,它保存了创建它的作⽤域的信息。JAVA并不能显式地⽀持闭包,但是在JAVA中,闭包可以通过“接⼝+内部类”来实现。  局部内部类:局部内部类就像是⽅法⾥⾯的⼀个局部变量⼀样,是不能有public、protected、private以及static修饰符的。23、string 转换成 integer的⽅式及原理  nt(string str)⽅法调⽤Integer内部的parseInt(string str,10)⽅法,默认基数为10,parseInt内部⾸先判断字符串是否包含符号(-或者+),则对相应的negative和limit进⾏赋值,然后再循环字符串,对单个char进⾏数值计算(char ch, intradix)在这个⽅法中,函数肯定进⼊到0-9字符的判断(相对于string转换到int),否则会抛出异常,数字就是如上⾯进⾏拼接然后⽣成的int类型数值。24、哪些情况下的对象会被垃圾回收机制处理掉?1. 没有引⽤指向;2. 只有弱引⽤指向并且不回收弱引⽤对象的话存储区⽆空间;3. 虚引⽤指向的对象;4. 不能通过GC Roots寻找到(Java虚拟机采⽤可达性原理回收时),以下⼏点对象可以被看做是GC Roots:  ● 虚拟机栈(栈桢中的本地变量表)中的引⽤的对象;  ● ⽅法区中的类静态属性引⽤的对象;  ● ⽅法区中的常量引⽤的对象;  ● 本地⽅法栈中JNI(Native⽅法)的引⽤的对象;25、讲⼀下常见编码⽅式?  常见的⼀些字符编码⽅式⽆⾮有:ASCII、拓展ASCII编码、Unicode编码集、GBK/GB2312/GB18030、UTF-8;ASCII编码:⽤来表⽰英⽂,它使⽤1个字节表⽰,其中第⼀位规定为0,其他7位存储数据,⼀共可以表⽰128个字符。拓展ASCII编码:⽤于表⽰更多的欧洲⽂字,⽤8个位存储数据,⼀共可以表⽰256个字符GBK/GB2312/GB18030:表⽰汉字。GBK/GB2312表⽰简体中⽂,GB18030表⽰繁体中⽂。Unicode编码:包含世界上所有的字符,是⼀个字符集。UTF-8:是Unicode字符的实现⽅式之⼀,它使⽤1-4个字符表⽰⼀个符号,根据不同的符号⽽变化字节长度。26、utf-8编码中的中⽂占⼏个字节;int型⼏个字节?  UTF-8最⼤的⼀个特点,就是它是⼀种变长的编码⽅式。它可以使⽤1~4个字节表⽰⼀个符号,根据不同的符号⽽变化字节长度。UTF-8的编码规则很简单,只有两条:1. 对于单字节的符号,字节的第⼀位设为0,后⾯7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。2. 对于n字节的符号(n>1),第⼀个字节的前n位都设为1,第n+1位设为0,后⾯字节的前两位⼀律设为10。剩下的没有提及的⼆进制位,全部为这个符号的unicode码。  少数是汉字每个占⽤3个字节,多数占⽤4个字节。int类型在java中占4个字节。27、静态代理和动态代理的区别,什么场景使⽤?28、Java的异常体系29、谈谈你对解析与分派的认识。30、修改对象A的equals⽅法的签名,那么使⽤HashMap存放这个对象实例的时候,会调⽤哪个equals⽅法?31、Java中实现多态的机制是什么?  java中实现多态的机制是依靠⽗类或接⼝的引⽤指向⼦类。从⽽实现了⼀个对象多种形态的特性。其中⽗类的引⽤是在程序运⾏时动态的指向具体的实例,调⽤该引⽤的⽅法时,不是根据引⽤变量的类型中定义的⽅法来运⾏,⽽是根据具体的实例的⽅法。32、如何将⼀个Java对象序列化到⽂件⾥?33、说说你对Java反射的理解34、说说你对Java注解的理解35、说说你对依赖注⼊的理解  依赖注⼊也叫依赖倒转原则,是java设计理论中⼀条⾮常著名的原则。其核⼼思想就是要将这种具体类之间的依赖,尽量转换成抽象依赖,也就是说类A应该依赖于抽象类IB,⽽不是具体的类B。这个注⼊的过程,通常是由⼀个控制程序来完成的,⽆需对象去关⼼,举例如下:11Public Person{ private ICar car; public Person(ICar onecar){ car=onecar; } public void drive(){ car.挂档; car.踩油门; car.打⽅向; }}这个时候,进⾏注⼊并且调⽤的过程,就很简单了,如下:123Car car=new Car ();Person boy=new Person(car);();36、说⼀下泛型原理,并举例说明  其实Java中的泛型是伪泛型,在编译期间,所有的泛型信息都会被擦除掉。在⽣成的Java字节码中是不包含泛型中的类型信息的。使⽤泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List和List等类型,在编译后都会编程List。JVM看到的只是List,⽽由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地⽅,但是仍然⽆法避免在运⾏时刻出现类型转换异常的情况。可以通过两个简单的例⼦,来证明java泛型的类型擦除:12345 ArrayList array1=new ArrayList();

("aaaa");

ArrayList array2=new ArrayList();

(10);

n(ss()==ss());   我们通过array1对象和array2对象的getClass⽅法获取它们的类的信息,最后发现结果为true。说明泛型类型String和Integer都被擦除掉了,只剩下了原始类型。123456 ArrayList arrayList3=new ArrayList();

(1);//这样调⽤add⽅法只能存储整形,因为泛型类型的实例为Integer

ss().getMethod("add", ).invoke(arrayList3, "asd");

for (int i=0;i<();i++) {

n((i));

}

  当我们利⽤反射调⽤add⽅法的时候,却可以存储字符串。这说明了Integer泛型实例在编译之后被擦除了,只保留了原始类型。37、Java中String的了解38、String为什么要设计成不可变的?1. 字符串常量池的需要   字符串常量池(String pool, String intern pool, String保留池) 是Java堆内存中⼀个特殊的存储区域, 当创建⼀个String对象时,假如此字符串值已经存在于常量池中,则不会创建⼀个新的对象,⽽是引⽤已经存在的对象。 假若字符串对象允许改变,那么将会导致各种逻辑错误,⽐如改变⼀个对象会影响到另⼀个独⽴对象。严格来说,这种常量池的思想,是⼀种优化⼿段。2. 允许String对象缓存HashCode   Java中String对象的哈希码被频繁地使⽤, ⽐如在hashMap 等容器中。字符串不变性保证了hash码的唯⼀性,因此可以放⼼地进⾏缓存.这也是⼀种性能优化⼿段,意味着不必每次都去计算新的哈希码。3. 安全性   String被许多的Java类(库)⽤来当做参数,例如 ⽹络连接地址URL,⽂件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。39、Object类的equal和hashCode⽅法重写,为什么?  因为当把Object对象放到集合中时,通过equals⽐较对象,不做处理还是会出现重复的问题,根据hash原则,对象的映射地址是根据算法⽣成,因为hash碰撞的存在,即两个不同的对象hash地址可能⼀样的情况,这样在hash地址相等的情况下还需要去重写equal⽅法进⾏⽐较。有两种办法可以解决这个问题,第⼀个就是重写Object类的equal和hashCode⽅法;第⼆个就是把对象转换成String再放⼊集合中,因为String类源码已经重写了这两个⽅法。

2023年6月20日发(作者:)

Android⾼级⼯程师⾯试题整理——java⾯试题 相信每次⾯试之前,⼤家都会⼤量刷⼀下⾯试题来应该对各种公司的⾯试吧,下⾯整理⼀下android⽅⾯的⾯试题分享给⼤家。本⽂主要分为以下⼏部分:Android⾯试题⾼级开发技术⾯试题跨平台Hybrid 开发1、Java中、hascode和==的区别  ==(双等号):对于基本数据类型(byte、short、char、int、long、float、double、boolean)来说,⽐较的是他们的值;对于引⽤类型(各种new出来的对象)来说⽐较的是他们的地址。  equals:默认情况(没有覆盖equals⽅法)下equals⽅法都是调⽤Object类的equals⽅法,⽽Object的equals⽅法是判断对象的内存地址。⼀般地都是覆盖这个⽅法,根据对象的内容是否相等来判断对象是否相等。如:String类就是覆盖这个⽅法,先判断对象的内存地址是否相等,之后再判断内容是否相等。  hascode:hascode()⽅法返回的就是⼀个数值,其⽬的是⽣成⼀个hash码,hash码的主要⽤途就是在对对象进⾏散列的时候作为key输⼊,我们需要每个对象的hash码尽可能不同,这样才能保证散列的存取性能。  在java中⼀般的equals()和hascode()通常要遵从如下⼏点约定,这样才能保证这两个⽅法在java中的⼀个规范,这也是为什么重写equals()⽅法需要重写hascode()⽅法的原因:1. 如果两个对象equals,java运⾏时环境会认为他们的hashcode⼀定相等;2. 如果两个对象不equals,他们的hashcode有可能相等。3. 如果两个对象hashcode相等,他们不⼀定hashcode。4. 如果两个对象hashcode不相等,他们⼀定不equals。2、int、char、long各占多少字节数  int占4个字节、char占2个字节、long占8个字节。3、int与integer的区别  int是基本数据类型,直接存数据。⽽integer是对象,⽤⼀个引⽤指向这个对象,是int的封装类。4、谈谈对java多态的理解  多态是指:⽗类引⽤指向⼦类对象,在执⾏期间会根据引⽤对象的实际类型,调⽤具体对象的相应⽅法。  ⼀般可以通过两种⽅式实现多态:重写、重载。  实现多态的三要素:集成、重写、⽗类引⽤指向⼦类对象。5、String、StringBuffer、StringBuilder区别 String:1. 是字符串常量,⼀旦创建就不能修改。对于已经存在了的String对象的修改都是重新创建⼀个新的对象,然后把新的值保存进去。2. String是final类,不能被继承。3. String覆盖了equals⽅法和hashCode⽅法。 StringBuffer:1. 是字符串可变对象,可以对字符串进⾏操作,修改字符串是不会新建⼀个对象。2. 执⾏效率较慢,但是线程安全。3. StringBuffer没有覆盖了equals⽅法和hashCode⽅法。 StringBuilder:1. 也是字符串可变对象,同StringBuffer⼀样,可以对字符串进⾏操作,也 不会新建对象。2. 执⾏效率⾼,但是线程不安全。6、什么是内部类?内部类的作⽤ 什么是内部类:   将⼀个类定义在另⼀个类⾥⾯或者⼀个⽅法⾥⾯,这样的类称为内部类。 作⽤:1. 解决多继承问题。在java中⼀个类只能单继承,⽽内类提供了⼀种多继承的解决⽅案。2. ⽅便将存在⼀定逻辑关系的组织在⼀起,⼜可以对外界隐藏。3. ⽅便编写事件驱动程序。4. ⽅便编写线程代码。7、抽象类和接⼝的区别1. 抽象类要被类继承,接⼝要被类实现;2. 接⼝只能做⽅法的声明,抽象类中可以作⽅法的声明,也可以作⽅法的实现;3. 接⼝⾥定义的变量只能是公共的静态常量,抽象类中的变量可以是普通变量;4. 接⼝是设计的结果,抽象类是重构的结果;5. 抽象类和接⼝都是⽤来抽象具体对象的,但是接⼝的抽象级别最⾼;6. 抽象类可以有具体的⽅法和属性,接⼝只能有抽象⽅法和常量;7. 抽象类主要⽤来抽象类别,接⼝主要⽤来抽象功能;8、抽象类的意义1. 为⼦类提供⼀个公共的类型;2. 封装⼦类中重复内容(成员变量和⽅法);3. 定义抽象⽅法,⼦类虽然有不同的实现,但改⽅法的定义是⼀致的。9、抽象类与接⼝的应⽤场景 接⼝的应⽤场景:1. 类与类之间需要特定的接⼝进⾏协调,⽽不在乎如何实现; 2. 作为能够实现特定功能的标识存在,也可以是什么接⼝⽅法都没有的纯粹标识。3. 需要将⼀组类视为单⼀的类,⽽调⽤者只通过接⼝来与这组类发⽣联系;4. 需要实现特定的多项功能,⽽这些功能之间可能完全没有任何联系; 抽象类的应⽤场景:1. 定义了⼀组接⼝,但⼜不想强迫每个实现类都必须实现所有的接⼝,可以⽤abstract定义⼀组⽅法,甚⾄可以是空⽅法体,然后有⼦类选择⾃⼰所感兴趣的⽅法来覆盖;2. 某些场合下,只靠纯粹的接⼝不能满⾜类与类之间的协调,还必须类中表⽰状态的变量来区别不同的关系。Abstract的中介作⽤可以很好地满⾜这⼀点;3. 规范了⼀组相互协调的⽅法,其中⼀些⽅法是共同的,与状态⽆关的,可以共享的,⽆需⼦类分别实现;⽽另⼀些⽅法却需要各个⼦类根据⾃⼰特定的状态来实现特定的功能;10、接⼝的意义1. 重要性:在java中,abstract class 和interface 是⽀持抽象类定义的两种机制,接⼝是特殊的抽象类,在java中⼀个类只能继承⼀个⽗类却可以实现多个接⼝。正是由于这两种机制的存在,才赋予了java强⼤的⾯向对象能⼒。java的特性封装、继承、多态。在java中有两种形式可以实现多态:继承和接⼝;2. 简单、规范性:如果⼀个项⽬⽐较庞⼤,那么就需要⼀个能理清所有业务的架构师来定义⼀些主要的接⼝,这些接⼝不仅告诉开发⼈员你需要实现哪些业务,⽽且也将命名规范限制住了(防⽌⼀些开发⼈员随便命名导致别的程序员⽆法看明⽩);3. 维护、拓展性:由于接⼝只做定义,⽽不关注实现,在某些功能模块中,可以通过接⼝去应⽤实现类,这能很好的降低功能模块之间的耦合性,从⽽修改某些模块的实现,不会影响到其他模块,⽅便后期的维护、拓展。4. 安全、严密性:接⼝是实现软件松耦合的重要⼿段,它描述了系统对外的所有服务,⽽不涉及任何具体的实现细节。这样就⽐较安全、严密⼀些。11、泛型中extends和super的区别  泛型中extends主要作⽤是类型的设定上界通配符,对应的为T及其⼦类对象;  泛型中super与extends是完全相反的,其定义是下界通配符,对应的为T及其⼦类对象;12、⽗类的静态⽅法能否被⼦类重写  不能。⽗类的静态⽅法能够被⼦类继承,但是不能够被⼦类重写,即使⼦类中的静态⽅法与⽗类中的静态⽅法完全⼀样,也是两个完全不同的⽅法。对于静态⽅法和静态变量来说,虽然在代码中可以通过⼦类对象来调⽤,当时在编译的时候就将其与类绑定在⼀起,既然它们在编译的时候就决定了调⽤的⽅法、变量,那就和重写没有关系了。重写指的是根据运⾏时对象的类型来决定调⽤哪个⽅法,⽽不是根据编译时的类型。13、进程和线程的区别进程是资源分配的最⼩单位,线程是资源调度的最⼩单位(程序执⾏的最⼩单位);进程有⾃⼰的独⽴地址空间,每启动⼀个进程,系统就会为它分配地址空间,建⽴数据表来维护代码段、堆栈段和数据段,这种操作⾮常昂贵。⽽线程是共享进程中的数据的,使⽤相同的地址空间,因此CPU切换⼀个线程的花费远⽐进程要⼩很多,同时创建⼀个线程的开销也⽐进程要⼩很多。线程之间的通信更⽅便,同⼀进程下的线程共享全局变量、静态变量等数据,⽽进程之间的通信需要以通信的⽅式(IPC)进⾏。但是多进程程序更健壮,多线程程序只要有⼀个线程死掉,整个进程也死掉了,⽽⼀个进程死掉并不会对另外⼀个进程造成影响,因为进程有⾃⼰独⽴的地址空间。14、final,finally,finalize的区别final:   在java中,final可以⽤来修饰类,⽅法和变量(成员变量或局部变量)。修饰类:当⽤final修饰类的时,表明该类不能被其他类所继承;修饰⽅法:即此⽅法不能被重写;修饰变量:表⽰常量,只能被赋值⼀次,赋值后其值不再改变;finally:   finally作为异常处理的⼀部分,它只能⽤在try/catch语句中,并且附带⼀个语句块,表⽰这段语句最终⼀定会被执⾏(不管有没有抛出异常,哪怕有return语句,都会执⾏),经常被⽤在需要释放资源的情况下。finalize   finalize()是在⾥定义的,也就是说每⼀个对象都有这么个⽅法。这个⽅法在gc启动,该对象被回收的时候被调⽤。其实gc可以回收⼤部分的对象(凡是new出来的对象,gc都能搞定,⼀般情况下我们⼜不会⽤new以外的⽅式去创建对象),所以⼀般是不需要程序员去实现finalize的。   特殊情况下,需要程序员实现finalize,当对象被回收的时候释放⼀些资源,⽐如:⼀个socket链接,在对象初始化时创建,整个⽣命周期内有效,那么就需要实现finalize,关闭这个链接。   ⼀个对象的finalize()⽅法只会被调⽤⼀次,⽽且finalize()被调⽤不意味着gc会⽴即回收该对象,所以有可能调⽤finalize()后,该对象⼜不需要被回收了,然后到了真正要被回收的时候,因为前⾯调⽤过⼀次,所以不会调⽤finalize(),产⽣问题。 所以,推荐不要使⽤finalize()⽅法,它跟析构函数不⼀样。15、堆与栈内存的区别  堆:是⼤家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是⽤户分配的空间。堆在操作系统对进程初始化的时候分配,运⾏过程中也可以向系统要额外的堆,但是记得⽤完了要还给操作系统,要不然就是内存泄漏。   栈:是个线程独有的,保存其运⾏状态和局部⾃动变量的。栈在线程开始的时候初始化,每个线程的栈互相独⽴,因此,栈是 thread safe的。操作系统在切换线程的时候会⾃动的切换栈,就是切换 SS/ESP寄存器。栈空间不需要在⾼级语⾔⾥⾯显式的分配和释放。16、序列化的⽅式1. 实现Serializable接⼝(隐式序列化):通过实现Serializable接⼝,这种是隐式序列化(不需要⼿动),这种是最简单的序列化⽅式,会⾃动序列化所有⾮static和 transient关键字修饰的成员变量。2. 实现Externalizable接⼝(显式序列化):Externalizable接⼝继承⾃Serializable, 我们在实现该接⼝时,必须实现writeExternal()和readExternal()⽅法,⽽且只能通过⼿动进⾏序列化,并且两个⽅法是⾃动调⽤的,因此,这个序列化过程是可控的,可以⾃⼰选择哪些部分序列化。3. 实现Serializable接⼝+添加writeObject()和readObject()⽅法。(显+隐序列化):先实现Serializable接⼝,并且添加writeObject()和readObject()⽅法。注意这⾥是添加,不是重写或者覆盖。但是添加的这两个⽅法必须有相应的格式。⽅法必须要被private修饰; ----->才能被调⽤第⼀⾏调⽤默认的defaultRead/WriteObject(); ----->隐式序列化⾮static和transient调⽤read/writeObject()将获得的值赋给相应的值; —>显式序列化17、Serializable 和Parcelable 的区别1、平台区别Serializable是属于 Java ⾃带的,表⽰⼀个对象可以转换成可存储或者可传输的状态,序列化后的对象可以在⽹络上进⾏传输,也可以存储到本地。Parcelable 是属于 Android 专⽤。不过不同于Serializable,Parcelable实现的原理是将⼀个完整的对象进⾏分解。⽽分解后的每⼀部分都是Intent所⽀持的数据类型。2、编写上的区别Serializable代码量少,写起来⽅便;Parcelable代码多⼀些,略复杂;3、选择的原则如果是仅仅在内存中使⽤,⽐如activity、service之间进⾏对象的传递,强烈推荐使⽤Parcelable,因为Parcelable⽐Serializable性能⾼很多。因为Serializable在序列化的时候会产⽣⼤量的临时变量, 从⽽引起频繁的GC。如果是持久化操作,推荐Serializable,虽然Serializable效率⽐较低,但是还是要选择它,因为在外界有变化的情况下,Parcelable不能很好的保存数据的持续性。4、本质的区别Serializable的本质是使⽤了反射,序列化的过程⽐较慢,这种机制在序列化的时候会创建很多临时的对象,⽐引起频繁的GC;Parcelable⽅式的本质是将⼀个完整的对象进⾏分解,⽽分解后的每⼀部分都是Intent所⽀持的类型,这样就实现了传递对象的功能了。18、静态属性和静态⽅法是否可以被继承?是否可以被重写?以及原因?  可以继承、不能重载;原因:   因为静态属性、⽅法从程序开始运⾏后就已经分配了内存。所有通过对象来访问该属性、⽅法的⽅式,都会在编译的时候就将其与类绑定在⼀起,就决定了调⽤的⽅法、变量,对象⽆关。19、静态内部类的设计意图20、成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项⽬中的应⽤21、谈谈对kotlin的理解22、闭包和局部内部类的区别  闭包:是能获取其他函数内部变量的函数。是⼀种能被调⽤的对象,它保存了创建它的作⽤域的信息。JAVA并不能显式地⽀持闭包,但是在JAVA中,闭包可以通过“接⼝+内部类”来实现。  局部内部类:局部内部类就像是⽅法⾥⾯的⼀个局部变量⼀样,是不能有public、protected、private以及static修饰符的。23、string 转换成 integer的⽅式及原理  nt(string str)⽅法调⽤Integer内部的parseInt(string str,10)⽅法,默认基数为10,parseInt内部⾸先判断字符串是否包含符号(-或者+),则对相应的negative和limit进⾏赋值,然后再循环字符串,对单个char进⾏数值计算(char ch, intradix)在这个⽅法中,函数肯定进⼊到0-9字符的判断(相对于string转换到int),否则会抛出异常,数字就是如上⾯进⾏拼接然后⽣成的int类型数值。24、哪些情况下的对象会被垃圾回收机制处理掉?1. 没有引⽤指向;2. 只有弱引⽤指向并且不回收弱引⽤对象的话存储区⽆空间;3. 虚引⽤指向的对象;4. 不能通过GC Roots寻找到(Java虚拟机采⽤可达性原理回收时),以下⼏点对象可以被看做是GC Roots:  ● 虚拟机栈(栈桢中的本地变量表)中的引⽤的对象;  ● ⽅法区中的类静态属性引⽤的对象;  ● ⽅法区中的常量引⽤的对象;  ● 本地⽅法栈中JNI(Native⽅法)的引⽤的对象;25、讲⼀下常见编码⽅式?  常见的⼀些字符编码⽅式⽆⾮有:ASCII、拓展ASCII编码、Unicode编码集、GBK/GB2312/GB18030、UTF-8;ASCII编码:⽤来表⽰英⽂,它使⽤1个字节表⽰,其中第⼀位规定为0,其他7位存储数据,⼀共可以表⽰128个字符。拓展ASCII编码:⽤于表⽰更多的欧洲⽂字,⽤8个位存储数据,⼀共可以表⽰256个字符GBK/GB2312/GB18030:表⽰汉字。GBK/GB2312表⽰简体中⽂,GB18030表⽰繁体中⽂。Unicode编码:包含世界上所有的字符,是⼀个字符集。UTF-8:是Unicode字符的实现⽅式之⼀,它使⽤1-4个字符表⽰⼀个符号,根据不同的符号⽽变化字节长度。26、utf-8编码中的中⽂占⼏个字节;int型⼏个字节?  UTF-8最⼤的⼀个特点,就是它是⼀种变长的编码⽅式。它可以使⽤1~4个字节表⽰⼀个符号,根据不同的符号⽽变化字节长度。UTF-8的编码规则很简单,只有两条:1. 对于单字节的符号,字节的第⼀位设为0,后⾯7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。2. 对于n字节的符号(n>1),第⼀个字节的前n位都设为1,第n+1位设为0,后⾯字节的前两位⼀律设为10。剩下的没有提及的⼆进制位,全部为这个符号的unicode码。  少数是汉字每个占⽤3个字节,多数占⽤4个字节。int类型在java中占4个字节。27、静态代理和动态代理的区别,什么场景使⽤?28、Java的异常体系29、谈谈你对解析与分派的认识。30、修改对象A的equals⽅法的签名,那么使⽤HashMap存放这个对象实例的时候,会调⽤哪个equals⽅法?31、Java中实现多态的机制是什么?  java中实现多态的机制是依靠⽗类或接⼝的引⽤指向⼦类。从⽽实现了⼀个对象多种形态的特性。其中⽗类的引⽤是在程序运⾏时动态的指向具体的实例,调⽤该引⽤的⽅法时,不是根据引⽤变量的类型中定义的⽅法来运⾏,⽽是根据具体的实例的⽅法。32、如何将⼀个Java对象序列化到⽂件⾥?33、说说你对Java反射的理解34、说说你对Java注解的理解35、说说你对依赖注⼊的理解  依赖注⼊也叫依赖倒转原则,是java设计理论中⼀条⾮常著名的原则。其核⼼思想就是要将这种具体类之间的依赖,尽量转换成抽象依赖,也就是说类A应该依赖于抽象类IB,⽽不是具体的类B。这个注⼊的过程,通常是由⼀个控制程序来完成的,⽆需对象去关⼼,举例如下:11Public Person{ private ICar car; public Person(ICar onecar){ car=onecar; } public void drive(){ car.挂档; car.踩油门; car.打⽅向; }}这个时候,进⾏注⼊并且调⽤的过程,就很简单了,如下:123Car car=new Car ();Person boy=new Person(car);();36、说⼀下泛型原理,并举例说明  其实Java中的泛型是伪泛型,在编译期间,所有的泛型信息都会被擦除掉。在⽣成的Java字节码中是不包含泛型中的类型信息的。使⽤泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List和List等类型,在编译后都会编程List。JVM看到的只是List,⽽由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地⽅,但是仍然⽆法避免在运⾏时刻出现类型转换异常的情况。可以通过两个简单的例⼦,来证明java泛型的类型擦除:12345 ArrayList array1=new ArrayList();

("aaaa");

ArrayList array2=new ArrayList();

(10);

n(ss()==ss());   我们通过array1对象和array2对象的getClass⽅法获取它们的类的信息,最后发现结果为true。说明泛型类型String和Integer都被擦除掉了,只剩下了原始类型。123456 ArrayList arrayList3=new ArrayList();

(1);//这样调⽤add⽅法只能存储整形,因为泛型类型的实例为Integer

ss().getMethod("add", ).invoke(arrayList3, "asd");

for (int i=0;i<();i++) {

n((i));

}

  当我们利⽤反射调⽤add⽅法的时候,却可以存储字符串。这说明了Integer泛型实例在编译之后被擦除了,只保留了原始类型。37、Java中String的了解38、String为什么要设计成不可变的?1. 字符串常量池的需要   字符串常量池(String pool, String intern pool, String保留池) 是Java堆内存中⼀个特殊的存储区域, 当创建⼀个String对象时,假如此字符串值已经存在于常量池中,则不会创建⼀个新的对象,⽽是引⽤已经存在的对象。 假若字符串对象允许改变,那么将会导致各种逻辑错误,⽐如改变⼀个对象会影响到另⼀个独⽴对象。严格来说,这种常量池的思想,是⼀种优化⼿段。2. 允许String对象缓存HashCode   Java中String对象的哈希码被频繁地使⽤, ⽐如在hashMap 等容器中。字符串不变性保证了hash码的唯⼀性,因此可以放⼼地进⾏缓存.这也是⼀种性能优化⼿段,意味着不必每次都去计算新的哈希码。3. 安全性   String被许多的Java类(库)⽤来当做参数,例如 ⽹络连接地址URL,⽂件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。39、Object类的equal和hashCode⽅法重写,为什么?  因为当把Object对象放到集合中时,通过equals⽐较对象,不做处理还是会出现重复的问题,根据hash原则,对象的映射地址是根据算法⽣成,因为hash碰撞的存在,即两个不同的对象hash地址可能⼀样的情况,这样在hash地址相等的情况下还需要去重写equal⽅法进⾏⽐较。有两种办法可以解决这个问题,第⼀个就是重写Object类的equal和hashCode⽅法;第⼆个就是把对象转换成String再放⼊集合中,因为String类源码已经重写了这两个⽅法。