java面试题(01)
1.什么是面向对象?
什么是面向对象?对比面向过程是俩种不同的处理问题的角度,面向过程更注重事情的每个顺序,而面向对象更注重事情有哪些参与者(对象),以及各自需要做什么。
比如:洗衣机洗衣服 面向过程会将任务拆解成一系列的步骤(函数)1.打开洗衣机—》2.放衣服–》放洗衣粉–》清洗—》烘干。 面向对象会拆出人和洗衣机俩个对象: 人:打开洗衣机 放衣服 放洗衣粉 洗衣机:清洗,烘干。
面向过程比较直接高效,面向对象更易于复用,扩展和维护
封装:封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项。
内部细节对外部调用透明,外部调用无需修改或者关系内部实现。
1.javabean的属性私有,提供getset对外访问,因为属性的赋值或者获取逻辑只能由javabean本身决定。而不能由外部胡乱修改。
2.orm框架
操作数据库,我们不需要关心链接是如如何建立的,sql是如何执行的,只需要引入mybatis,调方法即可。
继承
继承父类的方法,并做出自己的改变和扩展
子类共性的方法或者属性直接使用父类的,而不需要自己再定义,只需要扩展自己个性的
多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同
2.jdk、jre、jvm三者的之间的区别和联系
- jdk:java开发工具。
- jre:java运行时的环境(如果只想运行java的程序的时候我们只需要安装jre即可)
- jvm:java虚拟机。
jdk中包含java工具(javac,java,jconsole)、jre;
jre中包含jvm、lib(类库)。
三者呈现互相包含的样子。
3.==和equals
首先我先介绍一下堆栈。
内存堆栈
内存查找规则:取值时所有的变量从栈找如果栈里没有,试图补this。
成员变量和局部变量:局部变量必须初始化。局部变量的数据存在栈内存中’成员变量再方法外部,如果变量在堆中的对象里面,那么这个变量就是堆。
栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)
堆:存放所有new出来的对象
==对比的是栈中的值,基本数据类型是变量值、引用类型是对中内存对象的地址。
.equals 比较栈和堆中的对象。
4.简述一下final的作用
final(最终的)
- 修饰类:表示不可继承
- 修饰方法:表示方法不可被子类覆盖,但可以重载。
- 修饰变量:便是变量一但被赋值,就不可以更改它的值。
(1)修饰成员变量
- 如果final修饰的是类变量,只能在静态初始化块中指定初始值或者声明该类变量时指定初始值。
- 如果final修饰的是成员变量,可以在非静态初始化块,声明该变量或者构造器中执行初始值
(2)修饰局部变量
系统不会Wie局部变量进行初始化,局部变量必须由程序员显示初始化,因此使用final修饰局部变量的时候,即可以在定义时指定默认值(后面的代码不能对变量再赋值),也可以不指定默认值,而在后面代码中堆final变量赋初值(仅一次)。
(3)修饰基本类型数据和引用类型数据
- 如果是基本数据类型的变量,则其数据一旦在初始化之后,便不能更改;
- 如过是引用类型的变量。则在对其初始化之后便不能再让其指向另一个对象。但是引用的值是可变的。
为什么局部内部类和匿名内部类只能访问局部final变量?
首先需要知道的一点是:内部类和外部类是处于同一级别的,内部类不会因为定义在方法中就会随着方法的销毁跟着销毁。
这里就会产生问题:当外部类的方法结束时,局部变量就会被销毁了,但是内部类对象可能还存在(只有没有人再引用它是,才会死亡)这里就出现了一个矛盾:内部类对象访问了一个不存在的变量,为了解决这个问题,就讲就不变量复制一份作为内部类的成员变量,这样当局部变量死亡后,内部类仍可以访问它,实际访问的是局部变量copy,这样好像延长了局部变量的生命周期。
将局部变量复制为内部类的成员变量时,必须保证这俩个变量是一样的,也就是如果我们在内部类中修改了成员变量,方法中的局部变量也得跟着改变,怎么解决这个问题呢?
就将局部变量设置为final,对它初始化后,我就不让你再去修改这个变量,就保证了内部类的成员变量和方法的就不变量的一致性,这实际上也是一种妥协,使得局部变量与内部类内建立的拷贝保持一致。
5.string、stringbuffer、stringbuilder区别及使用场景
string是final修饰的,不可变,每次操作它的时候都会产生新的string对象。(不断的创建对象会占用内存)
stringbuffer和stringbuilder都是在原对象上操作(不会生成新对象)
stringbuffer是线程安全的,stringbuilde线程是不安全的。
性能:stringbuilder>stringbuffer>string
场景:经常需要改变字符串内容时使用stringbuffer、stringbuilder.
优先使用stringbuilder,多线程使用共享变量时使用stringbuffer。
synchronized(线程锁):多线程、共享变量,结果不会被更改,不会出现并发问题。
线程并发:交替执行 线程并行:同时执行 谁抢到线程执行谁。
6.重载和重写的区别
重载:发生在同一个类中,方法名必须相同,参数列表可以不同。
重写:发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小鱼等于父类,访问修饰范围大于等于父类,如果父类方法访问修饰符为private则子类不能重写该方法。
7.接口和抽象类的区别
- 接口类中可以存在普通成员函数,而几口中只能存在public abstract方法。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是pubic static final类型的。
- 抽象类只能继承一个,而接口可以实现多个。
接口的设计目的,是对类的行为进行约束(更准确的说是一种有约束,因为接口不能规定类不可以有什么行为),也就是提供一种机制,可以强制要求不同的类具有相同的行为,它只约束了行为的有无,但不对如何实现行为进行限制。
而抽象类的设计目的,是代码复用。当不同的类具有某些相同的行为(记为行为集合a),且其中一部分行为的实现方式一致时(a的非真子集,记为B),可以让这些类都派生一个抽象类,在这个抽象类中实现了B,避免让所有的子类来实现B,这就达到了代码复用的目的,而A减B的部分,留给各个子类子集实现,真是因为A-B在这里没有实现,所以抽象类不允许实例化出来…
8.List和set的区别
list:有序,按对象进去的顺序保存对象,可重复,允许多个null对象,可以使用iterator取出的所有元素,在逐一遍历,还可以使用get(int index),获取指定下标的元素。
set:无序,不可重复,最多允许一个null元素对象,取元素时只能用iterator接口取得所有元素,在逐一遍历各个元素。
9.Arraylist和LinkedList区别
Arraylist:基于动态数据,连续内存存储。适合下标访问(随机访问),扩容机制;因为数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动(往后赋值一份,插入新元素)使用尾插法并指定初始容量可以极大提升性能、甚至超过linkedlist(需要创建大量的node对象)
Linkedlist:基于链表,可以存储在分散的内存中,适合做数据插入及删除的操作,不适合查询;需要逐一遍历
遍历Linkedlist必须使用iterator不能使用for循环,因为每次for循环体内通过get(i)取得某一元素时需要对list重新进行遍历,性能消耗极大。
灵位不要师徒使用indexOf等返回元素索引,并利用其进行遍历,使用indexOf堆list进行了遍历,当结果为空时会遍历整个列表。
10.hashCode与equals
hashCode介绍
hashCode()的作用是获取哈希码,也称为散列码,它实际上是返回一个int证书,这个哈希码的作用是确定该对象在哈希表中的索引位置,hashcode()定义在jdk的object.java中,java中的任何类都包含有hashcode函数。散列表存储的是键值对keyvalue,它的特点是:能更具键快速的检查出对应的值,这其中就利用了散列码(可以快速找到所需要的对象)
为什么要有hashcode
以"Hashset如何检查重复"为例子来说明为什么要有hashCode:
对象加入HashSet时Hashset会先计算对象的hashcode值来判断对象加入的位置,如果没有,hashset会假设对象没有重复出现,但是如果发现有值,这时会调用equals方法来检查俩个对象是否真的相同,如果俩者相同,Hashset就不会让其加入操作成功,如果不同的化,就会重新散列到其他位置,这样就大大加少了equals的此时相应就大大提高了执行速度。
- 如果俩个对象相等,则hashcode一定也相同
- 俩个对象相等,对俩个对象分别调用equals方法都返回true
- 俩个对象有相同的hashcode值,他们也不一定是相等的。
- 因此equals方法被覆盖过,则hashcode方法也必须被覆盖
- hashcode的默认行为是对堆上的对象产生独特值,如果没有重写hashcode,则class的俩个对象如论如何都不会相等(即使俩个对象指向相同的数据)。