1.什么是面向对象
#概念,继承,封装,多态
2.继承和实现的区别 多实现的原理
继承:如果多个类的某个部分的功能相同,那么可以抽象出一个类出来,把他们的相同部分都放到父类里,让他们都继承这个类。
实现:如果多个类处理的目标是一样的,但是处理的方法方式不同,那么就定义一个接口,也就是一个标准,让他们的实现这个接口,各自实现自己具体的处理方法来处理那个目标。
联系:继承和接口都能实现代码重用,提高开发效率。提现了实物的传递性,继承关系达到复用的目的。
区别:单继承,多实现。在接口中只能定义全局常量(static final),和无实现的方法。
3.java中为什么要单继承,多实现
a.若为多继承,那么当多个父类中有重复的属性或者方法时,子类的调用父类相同的方法结果会含糊不清,因此用了单继承。
b.若实现的多个接口中有重复的方法也没关系,因为实现类中必须重写接口中的方法,所以调用时还是调用的实现类中重写的方法。
c.接口中,所有属性都是 static final修饰的,即常量。即使存在一定的冲突也会在编译时提示出错。
多实现的原理:接口中的方法和属性都是public abstract的,子类必须重写方法。动态绑定。
4.子父类
多态的实现基础是子父类重写,重载。
子父类可以有重写的方法,父类有private和default方法(子类访问不到)。子类可以用父类的重写、public、protected方法。子类可以有自己独特的方法。
类修饰符级别:public,protected,default,private。
private和构造方法不能被继承,即只能通过本类的对象调用,子类对象都不行。
final方法虽然可以被继承,但不能被重写(覆盖)。
static方法可以被子类继承,但是不能被子类重写(覆盖)。
如果多继承,子类构造函数该初始化哪个父类的构造呢。同名函数的调用时哪个呢。
5.静态绑定与动态绑定
绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来
所有私有方法、静态方法、构造器及初始化方法<clinit>都是采用静态绑定机制。在编译器阶段就已经指明了调用方法在常量池中的符号引用,JVM运行的时候只需要进行一次常量池解析即可。
动态绑定:编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。
如果子类改写了父类的方法,那么子类和父类的那些同名的方法共享一个方法表项。
方法调用步骤:根据对象得到该对象对应的方法表,根据偏移量15查看有无重写(override)该方法,如果重写,则可以直接调用(Girl的方法表的speak项指向自身的方法而非父类);如果没有重写,则需要拿到按照继承关系从下往上的基类(这里是Person类)的方法表,同样按照这个偏移量15查看有无该方法。
6.继承和接口的区别
继承只能单继承,接口可以多实现。
继承是增强父类的方法。
接口是丰富类的方法,让类拥有更多的方法(功能)。
接口不能有字段,接口不能带对象状态。
使用接口制定协议,然后用不同的实现来实现具体行为。
继承的两个类必须有父子关系,接口不必要则更灵活。
7.a=a+b, a+=b的区别
a=a+b 当类型不匹配的时候,编译器会通知你报错了。
a+=b 类型不一致的情况下,会自动转换,编译器不会报错。
在两个变量的数据类型一样时:a+=b 和a=a+b 是没有区别的。但是当两个变量的数据类型不同时,就需要考虑一下数据类型自动转换的问题了。
8. float 型 float f=3.4 是否正确
不正确。精度不准确,应该用强制类型转换,如下所示:float f=(float)3.4 或float f = 3.4f。在java里面,没小数点的默认是int, 小数默认是 double。
int转成long系统自动转换没有问题,因为后者精度更高,但是double转成float就不能自动转换了,所以后面得加上一个f。
数一下有效数字位数(整数位+小数位),7位以内的用float,15位以内的用double 但是还有一点小小的区别:
float f = (float) 62345678.912345; // => 6.234568E7 共 7 位
float f2 = (float) 12345678.912345; // => 1.2345679E7 共 8 位
(精度问题,float精度为7--8位,8位的情况是第一位是1,当是2时进位后面的精度丢失?)
9.i++和++i
i++ 先赋值再计算,++i 先计算再赋值。
五个i=++i,是i在内存自增一,然后进寄存器,最后赋值的时候i赋值就是1,第二个就是2,第五个就是5!而五个i=i++, 是赋值之后再自增1,一直i=0这个值先进寄存器,然后i在内存自增1,最后赋值的时候寄存器出来的0值会冲掉1值成为最终结果,所以无论多少个结果都会是0。
10.Java断言
断言概述,编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设。可以将断言看作是异常处理的一种高级形式。
断言可以有两种形式
a.assert Expression1
b.assert Expression1:Expression2
其中Expression1应该总是一个布尔值,Expression2是断言失败时输出的失败消息的字符串。
11.try,catch,finally中return语句
在以下4种特殊情况下,finally块不会被执行:
a、在finally语句块第一行发生了异常。 因为在其他行,finally块还是会得到执行
b、在前面的代码中用了System.exit(int)已退出程序。 exit是带参函数 ;若该语句在异常语句之后,finally会执行
c、程序所在的线程死亡。
d、关闭CPU
关于返回值:
如果try语句里有return,返回的是try语句块中变量值。 详细执行过程如下:
a、如果有返回值,就把返回值保存到局部变量中;
b、执行jsr指令跳到finally语句里执行;
c、执行完finally语句后,返回之前保存在局部变量表里的值。
d、如果try,finally语句里均有return,忽略try的return,而使用finally的return。
12.代码块初始化加载顺序。
父类静态,子类静态,父类构造,子类构造。
13.String类中有哪些方法
#charAt,contains,substring,split,toCharArray, valueOf, toUpperCase.
#String replace(char oldChar, char newChar), int indexOf(String str)
boolean startsWith(String prefix)测试此字符串是否以指定的前缀开始。
14.全部修饰符
类:public , abstract, final, 缺省, 不能有private。
方法:4个权限修饰符,static,final,synchronized,absract.
成员变量:volatile,不能有abstract。4个权限修饰符,static,final.
15.为什么少用反射
#不能使用JIT,性能下降。
反射的类型是动态解析的,这将导致JVM无法实施某些特定的优化(具体来说,就是我们常说的JIT优化),在性能敏感和频繁调用的方法上,应该尽量避免使用反射。
为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler),简称 JIT 编译器。之所以不一次性全部编译,是因为有一些代码只运行一次,没必要编译,直接解释运行就可以。如果编译成机器码那岂不是和 C、C++差不多了,不能跨平台“一次编译,到处运行”。
16、Object类有哪些方法
#equals, hashcode, clone, toString, getClass, wait/notify/notifyAll
object()空参构造,Java中规定在类定义过程中,对于未定义构造函数的类,默认会有一个无参数的构造函数,子类构造会默认调用父类的空构造。
clone()得到某一个对象的克隆,Object o2 = o1.clone()。
getClass()是一个native方法,返回的是此Object对象的类对象/运行时类对象Class<?>。效果与Object.class相同。Java中有专门定义了一个类,Class,去描述其他类所具有的特性,因此,从此角度去看,类本身也都是属于Class类的对象。为与经常意义上的对象相区分,在此称之为"类对象"。
equals() ==与equals在Java中经常被使用,大家也都知道==与equals的区别:==表示的是变量值完成相同(对于基础类型,地址中存储的是值,引用类型则存储指向实际对象的地址);equals表示的是对象的内容完全相同,就是用的==。重写equals()方法必须重写hasCode()方法。
hascode()方法返回一个整形数值,表示该对象的哈希码值。hashCode()方法的作用,其主要用于增强哈希表的性能。不相等的两个对象,hashCode()返回的哈希码可能相同。
toString()方法返回该对象的字符串表示。例如:com.corn.objectsummary.User@27ef。
wait(...) / notify() / notifyAll()方法,这几个方法主要用于java多线程之间的协作。wait():调用此方法所在的当前线程等待,直到在其他线程上调用此方法的主调(某一对象)的notify()/notifyAll()方法。
空finalize( ),Object中定义finalize方法表明Java中每一个对象都将具有finalize这种行为。
17、处理Hash冲突的方法。
#开放定址法,再哈希法,拉链法,公共溢出区
开放定址法:这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi。
再哈希法:这种方法是同时构造多个不同的哈希函数。发生Hash冲突时,采用不用的Hash函数技术哈希值。
链地址法:这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表。链地址法适用于经常进行插入和删除的情况。
建立公共溢出区:这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。
18.内部类
外部类调用内部类的方法,直接在外部类建立内部类的对象。
其它类调用内部类的方法,通过外部类对象建立内部类的对象。
19.StringBuilder,StringBuffer
#StringBuilder线性不安全,StringBuffer线性安全
在单线程情况下,如有大量的字符串操作情况,应该使用StringBuilder来操作字符串。不能使用String"+"来拼接而是使用,避免产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。如JSON的封装等。
在多线程情况下,如有大量的字符串操作情况,应该使用StringBuffer。
21.面向对象的设计原则
#单一职责,开闭,里式替换,依赖倒置,接口隔离,迪米特
单一职责原则——SRP
开闭原则——OCP:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
里式替换原则——LSP:通俗点说,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何异常。 但是反过来就不行了,因为子类可以扩展父类没有的功能,同时子类还不能改变父类原有的功能。
依赖倒置原则——DIP:高层模块不应该依赖底层模块,两者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
接口隔离原则——ISP:客户端不应该依赖它不需要的接口。
迪米特原则——LOD:一个对象应该对其他对象有最少的了解。
22.jdk1.8下bin中内容
其中bin目录里面存放了JDK的各种工具命令,即JDK开发工具的可执行文件。
javac.exe Java编译器,将Java源代码换成字节代码
java.exe Java解释器,直接从类文件执行Java应用程序代码
jar: jar文件管理工具,主要用于打包压缩、解压jar文件。
javap: Java反编译工具,主要用于根据Java字节码文件反汇编为Java源代码文件。
jconsole: 图形化用户界面的监测工具,主要用于监测并显示运行于Java平台上的应用程序的性能和资源占用等信息。
jvisualvm: JVM监测、故障排除、分析工具,主要以图形化界面的方式提供运行于指定虚拟机的Java应用程序的详细信息。
jmap: Java内存映射工具(Java Memory Map),主要用于打印指定Java进程、核心文件或远程调试服务器的共享对象内存映射或堆内存细节。
jstack: 堆栈跟踪工具,主要用于打印指定Java进程、核心文件或远程调试服务器的Java线程的堆栈跟踪信息Java。
jstat: JVM统计监测工具(JVM Statistics Monitoring Tool),主要用于监测并显示JVM的性能统计信息,包括gc统计信息。
jhat: Java堆分析工具(Java Heap Analysis Tool),用于分析Java堆内存中的对象信息。
jdb: Java调试工具(Java Debugger),主要用于对Java应用进行断点调试。
23.jdk1.8新特性
#lmada,接口增强,注解,日期
Lambda表达式(也称为闭包),它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理。
Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。往现存接口中添加新的(default)方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。
重复注解,一个方法或者类有多个功能时可重复注解。
Java 8引入了新的Date-Time API(JSR 310)来改进时间 日期的处理。
24.值传递和引用传递
值传递:在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容。
引用传递:”引用”也就是指向(直接)真实内容的地址值,在方法调用时,实参的地址通过方法调用被传递给相应的形参,在方法体内,形参和实参指向同一块内存地址,对形参的操作会影响的真实内容。
实参和形参的传递仅发生在栈帧之间。
引用传递,在Java中并不存在。无论是基本类型和是引用类型,在实参传入形参时,都是值传递,也就是说传递的都是一个(栈帧中的)副本,而不是内容本身。
局部变量是在栈帧中分配变量和值的。
25.深拷贝与浅拷贝
浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。
深拷贝:深拷贝把要复制的对象的变量和所引用的对象都复制了一遍。
26.序列化的底层怎么实现的
#字节序列持久化、远程通信,ObjectOutputStream 、FileOutputStream
Java序列化是指把Java对象转换为字节序列的过程(以便在网络上传输或者保存在本地文件中,实现了数据的持久化,实现了数据远程通信),而Java反序列化是指把字节序列恢复为Java对象的过程。
序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\object.out"));
oos.writeObject(new User("xuliugen", "123456", "male"));
反序列化
ObjectInputStream ois= new ObjectInputStream(new FileInputStream("object.out"));
User user = (User) ois.readObject();
27.int的范围
-2^31 2^31-1
28.字节与字符的区别
#储存单位,符号
字节(Byte):字节是通过网络传输信息(或在硬盘或内存中存储信息)的单位。1个字节等于8位二进制,它是一个8位的二进制数,是一个很具体的存储空间。
字符:ANSI中的字符采用8bit,而UNICODE中的字符采用16bit。按照ANSI编码标准,标点符号、数字、大小写字母都占一个字节,汉字占2个字节。按照UNICODE标准所有字符都占2个字节。
29.基本语法判断
Integer r = new Integer(100);
Integer r2 = new Integer(100);
False true
String s = ""; 建立一个新的引用,会分配内存,内存中储存空串。
String s2 = null; 建立一个空的引用,不会分配内存。
False false
30. BIO NIO AIO说一下?epoll了解吗?用过吗?具体调用OS什么方法?webSocket呢?
Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 IO 模型的封装。程序员在使用这些 API 的时候,不需要关心操作系统层面的知识,也不需要根据不同操作系统编写不同的代码。只需要使用Java的API就可以了。
同步与异步
同步: 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。
异步: 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。
阻塞和非阻塞
阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。
举个生活中简单的例子,你妈妈让你烧水,小时候你比较笨啊,在那里傻等着水开(同步阻塞)。等你稍微再长大一点,你知道每次烧水的空隙可以去干点其他事,然后只需要时不时来看看水开了没有(同步非阻塞)。后来,你们家用上了水开了会发出声音的壶,这样你就只需要听到响声后就知道水开了,在这期间你可以随便干自己的事情,你需要去倒水了(异步非阻塞)。
BIO (Blocking I/O)
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
NIO 简介
NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
AIO 是异步IO的缩写,AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。
31.同步和阻塞的区别
对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回,它还会抢占cpu去执行其他逻辑,也会主动检测io是否准备好。同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。
在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。