1.使用length属性获取数组长度
数组求长度用length属性
字符串求长度用length()方法
集合求长度用size()方法
2. public、private、protected、friendly区别
在说明这四个关键字之前,我想就class之间的关系做一个简单的定义,对于继承自己的class,base class可以认为他们都是自己的子女,而对于和自己一个目录下的classes,认为都是自己的朋友。
1、public:public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用
2、private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以直接使用,私有财产神圣不可侵犯嘛,即便是子女,朋友,都不可以使用。
3、protected:protected对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private。
作用域 当前类 同一package 子孙类 其他package
public √ √ √ √
protected √ √ √ ×
friendly √ √ × ×
private √ × × ×
注:不写时默认为friendly
3.String s=new String("xyz");创建了几个String Object?二者之前的区别是什么?
两个。第一个对象是字符串常量"xyz" 第二个对象是new String("xyz")的时候产生的,在堆中分配内存给这个对象,只不过这个对象的内容是指向字符串常量"xyz" 另外还有一个引用s,
指向第二个对象。这是一个变量,在栈中分配内存。
4.short s1; s1=s1+1;是否有错
对于short s1=1; s1=s1+1; 由于s1+1运算的时候会自动提升表达式的类型,所以结果是int型,再赋值给short类型s1时候,编译器件将报告需要强制转换类型的错误。。。
对于short s1=1; s1+=1;;由于+=是java语言规定的运算符,java编译器会对它进行特殊的额处理,因此可以正确的编译。。
5.Overriding和Overloading区别
6、Set里面的元素不能重复,用什么方法区分重复与否。
==是用来判断两者是否是同一对象(同一事物),而equals是用来判断是否引用同一个对象。再看一下Set里面存的是
对象,还是对象的引用。根据java的存储机制可知,set里面存放的是对象的引用,所以当两个元素只要满足了equals()时就已经指向同一个对象,
也就出现了重复元素。所以应该用equals()来判断。
7、给出一个常见的runtime exception。
Java.lang.NullPointerException空指针异常
Java.lang.IndexOutOfBoundsException索引超出异常
Java.lang.ArithmeticException算术异常
Java.lang.ClassCastException类转换异常
IllegalArgumentException非法数据异常
IllegalStateException非法语句异常
8、error和exception区别。
Exception和Error都是继承了Throwable类,在java中只有Throwable类型的实例才可以被抛出(throw)或者捕获(catch),他是异常处理机制的基本组成类型。
Exception和Error体现了java平台设计者对不同异常情况的分类,Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应的处理。
Error是指正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序(比如JVM自身)处于非正常状态,不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之类,都是Error的子类。
Exception又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源码里必须显示的进行捕获处理,这里是编译期检查的一部分。前面我们介绍的不可查的Error,是Throwable不是Exception。
不检查异常就是所谓的运行时异常,类似NullPointerException,ArrayIndexOutOfBoundsExceptin之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译器强制要求。
9、List和Set是否继承自Collection接口。
List,Set是,Map不是
Collection
--List:将以特定次序存储元素。所以取出来的顺序可能和放入顺序不同。
--ArrayList / LinkedList / Vector
--Set : 不能含有重复的元素
--HashSet / TreeSet
Map
--HashMap
--HashTable
--TreeMap
详细介绍:
List特点:元素有放入顺序,元素可重复
Map特点:元素按键值对存储,无放入顺序
Set特点:元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的)
List接口有三个实现类:LinkedList,ArrayList,Vector
LinkedList:底层基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还存储下一个元素的地址。链表增删快,查找慢
ArrayList和Vector的区别:ArrayList是非线程安全的,效率高;Vector是基于线程安全的,效率低
Set接口有两个实现类:HashSet(底层由HashMap实现),LinkedHashSet
SortedSet接口有一个实现类:TreeSet(底层由平衡二叉树实现)
Query接口有一个实现类:LinkList
Map接口有三个实现类:HashMap,HashTable,LinkeHashMap
HashMap非线程安全,高效,支持null;HashTable线程安全,低效,不支持null
SortedMap有一个实现类:TreeMap
其实最主要的是,list是用来处理序列的,而set是用来处理集的。Map是知道的,存储的是键值对
set 一般无序不重复.map kv 结构 list 有序
15、值传递和引用传递
- 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
- 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 所以对引用对象进行操作会同时改变原对象。
一般认为,java内的传递都是值传递。
16、switch是否作用在byte、long、string上。
-
switch可作用于char byte short int
-
switch可作用于char byte short int对应的包装类
-
switch不可作用于long double float boolean,包括他们的包装类
-
switch中可以是字符串类型,String(JDK1.7之后才可以作用在string上)
-
switch中可以是枚举类型(JDK1.5之后)
17、ArrayList和Vector区别,HashMap和Hashtable区别(了解这几个类的底层jdk中的编码方式)
ArrayList和Vector都是以数组的形式存储。ArrayList可以存null值,Vector是不可以的,vector是线程安全的,ArrayList是线程不安全的,但是可以用collections.sychronizedList()实现线程同步,ArrayList在容器充满时会自动扩容50%,而vector则扩容100%,ArrayList则更节省空间;
hashMap:key和value可以为null值,非线程安全,继承abstractMap接口,重新计算hash值
hashTable:key和value不能为null值,线程安全,继承dictionary接口,hash值直接使用key.hashCode()
现在推荐使用concurrentHashMap替代hashTable,因为concurrentHashMap使用的是局部锁技术,吧map分为多个segment,而hashtable锁的机制是对整个对象的加锁,concurrenthashmap的性能优于hashTable。
18、GC是什么,为什么要有GC,简单介绍GC。
GC是垃圾回收,内存处理是开发人员容易出现问题的地方,错误或忘记内存回收都可能会使得系统不稳定或者崩溃。而Java就提供了一个垃圾回收功能,可以监测对象是否超过作用域或者说是不可达的状态,从而达到自动回收内存的目的。
垃圾回收可以有效的防止内存泄漏,垃圾回收器是一个单独的低优先级的线程,在不可预知的情况对已经死亡或者很久没有使用的对象进行回收,程序是不能实时的调用垃圾回收器对某个对象进行回收。
垃圾回收的实现有很多方法,其中有一种是用分代回收算法实现的。分代回收是将堆空间划分为三个区:年轻代、年老代和永久代。年轻代和年老代存储的是动态产生的对象,而永久代存储的是类的信息,包括解析到的方法、变量等,永久带几乎是不参与垃圾回收的。
年轻代又分为三个区,一个eden区,两个相同的survivor区,这样分主要是为了让生命周期短的对象尽量留在年轻代。新创建的对象都会先存放到eden区,直到eden申请不到空间时,进行minorGC,将存活的对象拷贝到survivor区,年老代一般存储的是生命周期比较长的对象。
垃圾回收过程如下:
1.在eden区完成内存分配
2.eden区满了再创建对象时,会因为申请不到空间而出发minorGC,对eden+1survivor进行垃圾回收
3.minorGC时,将eden区不能回收的对象拷贝到空的survivor区,另一个survivor中不能回收的对象也会放到这个空的survivor区,最后会清空eden区并保证一个survivor是空的
4.执行到第3步的时候,若survivor满的,则这些对象会被拷贝到年老代(old)区,或者是survivor并没有满,但有些对象足够old,则这些对象会被放到年老代区
5.当年老代满了以后,会进行fullGC
19、float f=3.4是否正确。
不正确。在Java中,没有小数点的默认是int型,有小数点的默认是double型,若要定义一个float型的数应该强制转换类型,或者在数字后面加个“f”。
22、String和Stringbuffer的区别
String:
1.是对象不是原始类型。
2.为不可变对象,一旦被创建,就不能修改它的值。
3.对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去。
4.String是final类,即不能被继承。
StringBuffer:
1.是一个可变对象,当对它进行修改的时候不会像String那样重新建立对象。
2.它只能通过构造函数来建立,StringBuffer subffer=new StringBuffer();
3.对象被建立以后,在内存中就会分配内存空间,并初始保存一个null,通过它的append方法向其赋值 subffer.append(“hello word”);
字符串连接操作中StringBuffer的效率要明显比String高;
String对象是不可变对象,每次操作String都会建立新的对象来保存新的值。
StringBuffer对象实例化后,只对这一个对象操作。
23、final、finally、finalize区别
一、final :
1、修饰符bai(关键字) 如果一个类被声明为dufinal,意味着它不能再派生新的zhi子类,不能作为dao父类被继承。因此一个类不能及被声明为abstract,又被声明为final的。
2、将变量或方法声明为final,可以保证他们使用中不被改变。被声明为final的变量必须在声明时给定初值,而以后的引用中只能读取,不可修改,被声明为final的方法也同样只能使用,不能重载。
二、finally:
在异常处理时提供finally块来执行清楚操作。如果抛出一个异常,那么相匹配的catch语句就会执行,然后控制就会进入finally块,如果有的话。
三、finalize:
是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除之前做必要的清理工作。这个方法是在垃圾收集器在确定了,被清理对象没有被引用的情况下调用的。
finalize是在Object类中定义的,因此,所有的类都继承了它。子类可以覆盖finalize()方法,来整理系统资源或者执行其他清理工作。
24、面向对象的特征
抽象:提取现实世界中某事物的关键特性,为该事物构建模型的过程。对同一事物在不同的需求下,需要提取的特性可能不一样。得到的抽象模型中一般包含:属性(数据)和操作(行为)。这个抽象模型我们称之为类。对类进行实例化得到对象。
封装:封装可以使类具有独立性和隔离性;保证类的高内聚。只暴露给类外部或者子类必须的属性和操作。类封装的实现依赖类的修饰符(public、protected和private等)
继承:对现有类的一种复用机制。一个类如果继承现有的类,则这个类将拥有被继承类的所有非私有特性(属性和操作)。这里指的继承包含:类的继承和接口的实现。
多态:多态是在继承的基础上实现的。多态的三个要素:继承、重写和父类引用指向子类对象。父类引用指向不同的子类对象时,调用相同的方法,呈现出不同的行为;就是类多态特性。多态可以分成编译时多态和运行时多态。
抽象、封装、继承和多态是面向对象的基础。在面向对象四大基础特性之上,我们在做面向对象编程设计时还需要遵循有一些基本的设计原则。
28、heap和stack区别
java 的内存分为两类,一类是栈内存,一类是堆内存。栈内存是指程序进入一个方法时,
会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法
结束时,分配给这个方法的栈会释放,这个栈中的变量也将随之释放。
堆是与栈作用不同的内存,一般用于存放不放在当前方法栈中的那些数据,例如,使用 new
创建的对象都放在堆里,所以,它不会随方法的结束而消失。 方法中的局部变量使用 final
修饰后,放在堆中,而不是栈中。
区别:
1.heap是堆,stack是栈。
2.stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的,heap常用new关键字来分配。
3.stack空间有限,heap的空间是很大的自由区。
在Java中,
若只是声明一个对象,则先在栈内存中为其分配地址空间,
若再new一下,实例化它,则在堆内存中为其分配地址。
4.举例:
数据类型 变量名;这样定义的东西在栈区。
如:Object a =null; 只在栈内存中分配空间
new 数据类型();或者malloc(长度); 这样定义的东西就在堆区
如:Object b =new Object(); 则在堆内存中分配空间
29、Java中的异常处理机制的简单原理和应用
异常指Java程序运行时(非编译)所发生的非正常情况或错误。
java对异常进行了分类,不同类型的异常使用了不同的java类,所有异常的根类为java.lang.Throwable.Throwable派生了2个子类:Error和Exception.
Error表示程序本身无法克服和恢复的一种严重错误,程序只有死的份,如内存溢出和死锁问题等系统问题。
Exception表示还能克服和恢复,其中又分为系统异常和普通异常。系统异常是软件本身缺陷导致的问题,也就是软件开发问题考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但这种情况下可以选择让软件继续运行或死掉。如数组越界问题(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException),类转换异常(ClassCastException);普通异常是运行环境的变化或异常导致的问题,是用户能够克服的问题,如网路掉线、硬盘空间不足、IO异常发生这种异常后程序不应该死掉。
java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try..catch处理或throws声明继续抛给上层调用方法处理。所以普通异常为checked异常,而系统异常可以处理也可以不处理。编译器不强制用try..catch或throws声明,所以系统异常成为uncheckde异常。
请写出你最常见到的5个runtime exception。 常见异常见:http://www.runoob.com/java/java-exceptions.html
5个RuntimeException:
NullPionterException
ArrayIndexOutOfBoundsException
StringIndexOutOfBoundsException
ClassCastException
NumberFormatException
32、描述一下JVM加载Class文件的原理和机制
https://www.cnblogs.com/mengchunchen/p/7845163.html
33、排序的几种方法,了解。
https://www.cnblogs.com/diligentYe/p/6675339.html
34、Java语言如何进行异常处理,throws,throw,try catch finally代表什么意义,try块中可以抛出异常吗
Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其它子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理。
用try来指定一块预防所有”异常”的程序。紧跟在try程序后面,应包含一个catch子句来指定你想要捕捉的”异常”的类型。
throw语句用来明确地抛出一个”异常”。
throws用来标明一个成员函数可能抛出的各种”异常”。
Finally为确保一段代码不管发生什么”异常”都被执行一段代码。
可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try语句,”异常”的框架就放到堆栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种”异常”进行处理,堆栈就会展开,直到遇到有处理这种”异常”的try语句。
35、一个’.java’源文件是否可以包括多个类,有什么限制。
可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。一个文件中可以只有非public类,如果只有一个非public类,此类可以跟文件名不同。
问题一:为什么类之中只能有一个public的类?
每个编译单元(文件)都只有一个public 类。因为每个编译单元都只能有一个公共接口,用public类来表现。该接口可以按照要求包含众多的支持包访问权限的类。如果有一个以上的public 类,编译器就会报错。 并且public类的名称必须与文件名相同(严格区分大小写)。 当然一个编译单元内也可以没有public类。在PUBLIC类中找程序的入口 main函数 你想想如果很多PUBLIC 类,那程序从何运行!
问题二:public 类的名称为什么要与文件名相等?
首先Java是被解释执行的。它在运行时并不是将所有的class文件全都放到内存中。而是在遇到import的时候才去相应的文件目录找相应的class文件。对于一个public类,它是可以被项目中任何一个类所引用的,只需在使用它前import一下它所对应的class文件即可。将类名与文件名一一对应就可以方便虚拟机在相应的路径(包名)中找到相应的类的信息。如果不这么做的话,就很难去找,而且开销也会很大。
36、Java中有几种类型流,jdk为每种类型的流提供了一些抽象类以供继承,请分别说出它们是哪些类。
字节流,字符流。
字节流继承于InputStream OutputStream,
字符流继承于InputStreamReader OutputStreamWriter。
在java.io包中还有许多其他的流,主要是为了提高性能和使用方便
37、Java中会存在内存泄漏吗,请简单描述。
https://blog.csdn.net/smile0198/article/details/21650339
38、静态变量和实例变量的区别。
语法定义上的区别:静态变量前要加static关键字,而实例变量前不加。
在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码文件,不用创建任何实例象,静态变量就会被分配空间,静态变量就可以被使用了。
总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
39、什么是Java序列化,如何实现java序列化。
https://www.cnblogs.com/shoshana-kong/p/10538661.html
40、是否可以从一个static方法内部发生对非static方法调用。
不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在
该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。也就是
说,当一个static方法被调用时,可能还没有创建任何实例对象,如果从一个static方法中
发出对非static方法的调用,那个非static方法关联到那个对象上的呢?这个逻辑无法成
立,所以,一个static方法内部不能发出对非static方法的调用。
static方法是静态方法,是属于类的方法
非static方法是属于对象的方法,所以在static方法中想要调用非static方法,要先新创建一个对象,再有这个对象来调用非static方法。
43、说出常用类、包、接口,各举5个。
https://blog.csdn.net/XueBaiBaiBai/article/details/81175702
44、Java中实现线程的方法,用关键字修饰同步方法。
有两种实现方法,分别是继承Thread类与实现Runnable接口
用synchronized关键字修饰同步方法
反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程
45、同步和异步区别。
同步:
同步的思想就是用户首先发送一个请求,那么服务器将要把用户的请求都处理完成后,在会通知用户,那么这样就会造成用户等待实践太长,比如说我们在浏览器点了一个功能,那么点击了以后用户没有反应,其实这时候服务器正在处理用户发送的请求,暂时没回应用户,这种情况对于用户来说就是 卡死了.... 其实它还是正在工作中,那么如果用户关闭了浏览器,那么它正在执行中的操作将会断开。
异步:
将用户发送的请求放入消息队列,并反馈给用户已经执行成功,这时候并不是真正的执行成功了,而是先通知用户执行成功了,当用户看到执行成功的结果,那么它肯定不会在执行一次了,这时候消息队列在慢慢的把用户发送的请求写到服务器中。
总结:
同步:是所有的操作都做完,写入服务器数据库当中才会通知用户执行成功,这样的话会造成服务器压力过大,而且用户的体验效果也不是很好。
异步:不用等待服务器数据库是否写入,而是先通知用户执行成功,随后在慢慢的写入服务器数据库,这样会减轻服务器的压力,同时对用户的体验效果很好。
46、线程同步的方法。
一、synchronized关键字
https://blog.csdn.net/jiangshangchunjiezi/article/details/88118063
此关键字修饰方法,当方法体规模很大时,会影响程序的执行效率,为了提高效率,出现了synchronized块
二、wait()方法与notify()方法
wait、notify使用:https://blog.csdn.net/jiangshangchunjiezi/article/details/97168265
三、lock
https://blog.csdn.net/jiangshangchunjiezi/article/details/88670906
四、volatile
特殊域变量(volatile)实现线程同步
https://blog.csdn.net/jiangshangchunjiezi/article/details/88662511
1. 新建状态(New) : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
3. 运行状态(Running) : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4. 阻塞状态(Blocked) : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
(02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead) : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
wait():作用是让当前线程进入等待状态,同时,也会将当前线程释放它所持有的锁。直到其他线程调用此对象的notify()方法或notifyAll方法,当前线程被唤醒(进入就绪状态)
notify():唤醒当前对象上的线程
wait(long timeout):让当前线程处于“阻塞状态”,直到其他线程调用此对象的notify()或notifyAll()方法,或者超过指定时间量,当前线程被唤醒(进入就绪状态)
①直接隶属于Object类,即所有对象都会有这一对方法。因为这一对方法阻塞时要释放占用的锁
最后,关于 wait() 和 notify() 方法再说明三点:
调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题
除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。
wait()和notify()必须成对存在。
sleep和wait的区别有:
1,这两个方法来自不同的类分别是Thread和Object
2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
问:接口和抽象类的区别,什么场景下使用
不同:
抽象类:
1.抽象类中可以定义构造器
2.可以有抽象方法和具体方法
3.接口中的成员全都是 public 的
4.抽象类中可以定义成员变量
5.有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法
6.抽象类中可以包含静态方法
7.一个类只能继承一个抽象类
接口:
1.接口中不能定义构造器
2.方法全部都是抽象方法
3.抽象类中的成员可以是 private、默认、protected、public
4.接口中定义的成员变量实际上都是常量
5.接口中不能有静态方法
6.一个类可以实现多个接口
相同:
1.不能够实例化
2.可以将抽象类和接口类型作为引用类型
3.一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要
被声明为抽象类
什么时候该用抽象类?什么时候该用接口?
实际上,判断的标准很简单。如果要表示一种 is-a 的关系,并且是为了解决代码复用问题,我们就用抽象类;如果要表示一种 has-a 关系,并且是为了解决抽象而非代码复用问题,那我们就用接口。
问:代理模式,单例模式,工厂模式,使用场景
单例模式:
定义:一个类只允许创建一个对象(或者叫实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式
特点:1、构造方法私有化;2、实例化的变量引用私有化;3、获取实例的方法共有。
用途:从业务概念上,有些数据在系统中只应该保存一份,就比较适合设计为单例类。比如,系统的配置信息类,连接池类、ID 生成器类。除此之外,我们还可以使用单例解决资源访问冲突的问题。
实现:
饿汉式 :饿汉式的实现方式,在类加载的期间,就已经将 instance 静态实例初始化好了,所以,instance 实例的创建是线程安全的。不过,这样的实现方式不支持延迟加载实例。
懒汉式 :懒汉式相对于饿汉式的优势是支持延迟加载。这种实现方式会导致频繁加锁、释放锁,以及并发度低等问题,频繁的调用会产生性能瓶颈。
双重检测双重检测实现方式既支持延迟加载、又支持高并发的单例实现方式。只要 instance 被创建之后,再调用 getInstance() 函数都不会进入到加锁逻辑中。所以,这种实现方式解决了懒汉式并发度低的问题。
静态内部类利用 Java 的静态内部类来实现单例。这种实现方式,既支持延迟加载,也支持高并发,实现起来也比双重检测简单。
枚举最简单的实现方式,基于枚举类型的单例实现。这种实现方式通过 Java 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。
工厂模式:
使用:当创建逻辑比较复杂,是一个“大工程”的时候,我们就考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用相分离
第一种情况:类似规则配置解析的例子,代码中存在 if-else 分支判断,动态地根据不同的类型创建不同的对象。针对这种情况,
我们就考虑使用工厂模式,将这一大坨 if-else 创建对象的代码抽离出来,放到工厂类中。还有一种情况,尽管我们不需要根据不同的类型创建不同的对象,
但是,单个对象本身的创建过程比较复杂,比如前面提到的要组合其他类对象,做各种初始化操作。在这种情况下,我们也可以考虑使用工厂模式,将对象的创建过程封装到工厂类中。
使用工厂模式标准:
封装变化:创建逻辑有可能变化,封装成工厂类之后,创建逻辑的变更对调用者透明。
代码复用:创建代码抽离到独立的工厂类之后可以复用。
隔离复杂性:封装复杂的创建逻辑,调用者无需了解如何创建对象。
控制复杂度:将创建代码抽离出来,让原本的函数或类职责更单一,代码更简洁。
代理模式:
静态代理和动态代理区别:静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的“重复”代码,
增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。
我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,
然后在系统中用代理类替换掉原始类。
应用场景:Aop实现中用到了JDK的动态代理;
问:redis为什么快
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
Redis 提供了两种持久化方式:RDB(默认) 和AOF
比较:
1、aof文件比rdb更新频率高,优先使用aof还原数据。
2、aof比rdb更安全也更大
3、rdb性能比aof好
4、如果两个都配了优先加载AOF
redis通讯协议(RESP ),能解释下什么是RESP?有什么特点?
RESP 是redis客户端和服务端之前使用的一种通讯协议;
RESP 的特点:实现简单、快速解析、可读性好
什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?
缓存穿透
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。
缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
三、redis的命中率
用户在访问缓存中的数据时,并不是100%会返回的,可能有不返回的情况,这种情况下就得去DB查询数据,为了提高系统的性能,我们需要提高缓存的命中率。redis中info
命令可以查询到基本信息。
命中率 = keyspace_hits / (keyspace_misses + keyspace_hits)
合理的redis配置可以提高命中率
问:spring循环依赖
比如X依赖Y,Y依赖X
bean是由beanDefinition来构建的,bean的生命周期(
spring容器启动扫描,
把bean变为beanDefinition,存入beanDefinitionMap中,
然后进行遍历,遍历之后,然后对beanDefinition做基本验证,是否单例,是否抽象,是否懒加载,
然后实例化这个bean,先去spring单例池中找,没有就去实例化bean的X对象,属性填充,因为X依赖Y,就会顺序去创建Y,但是当Y属性填充,发现依赖于X,X没有被完整实例化,再去走X的创建。)
解决方法:
1.在实例变量上使用@Autowired注解,让Spring决定在合适的时机注入,而非在初始化类的时候就注入。
2.用基于setter方法的依赖注入取代基于构造函数的依赖注入来解决循环依赖。
3.懒加载@lazy
问:CPU过高问题定位:
top+jstack
top命令查看cpu占用最高的进程,top -Hp pid 查看该进程下线程信息,找到最高的线程pid,然后十进制转换为十六进制,jstack pid > 1.txt 然后就可以找到出问题的线程,和对应的代码。
JMC
可能导致问题及解决方式:
无限while,频繁GC,频繁创建新的对象(合理使用单例),序列化和反序列化(使用合理的API)
mysql语句问题排查:
1.show processlist 语句,查找负荷最重的 SQL 语句,优化该SQL,比如适当建立某字段的索引;
2.explain :分析sql的全过程,查看type,看是否进行全表扫描,然后看possible_keys,key这些查看索引,rows扫描行数
3.在多用户,高并发的情况下,任何系统都会hold不住的,所以,使用缓存是必须的,memcached或者redis都可以;
SQL 优化中,就如下三点:
最大化利用索引。
尽可能避免全表扫描。
减少无效数据的查询。
1.避免不走索引的场景
2.避免select *
3.多表关联查询 小表在前 大的在后
4.多个表查询时 使用表明在查询字段前面
5.where 替换having
6.用join代替 in/not in
7.拆分复杂sql为多个小sql
8.合理使用分页
使用索引时有些不生效的情况
1、使用like关键字模糊查询时,% 放在前面索引不起作用,只有“%”不在第一个位置,索引才会生效(like ‘%文’–索引不起作用)
2、使用联合索引时,只有查询条件中使用了这些字段中的第一个字段,索引才会生效
3、使用OR关键字的查询,查询语句的查询条件中只有OR关键字,且OR前后的两个条件中的列都是索引时,索引才会生效,否则索引不生效。
4、尽量避免在where子句中使用!=或<>操作符,否则全表扫描。
5、对查询进行优化,应尽量避免全表扫描,首先应考虑在where以及order by涉及的列上建立索引。
6、应尽量避免在 where 子句中对字段进行表达式操作,否则全表扫描。
7、尽量避免在where子句中对字段进行函数操作,否则全表扫描。
8、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
9、并不是所有的索引对查询都有效,sql是根据表中的数据来进行查询优化的,当索引列有大量数据重复时,sql查询不会去利用索引
10、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,
因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,
11、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。
这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
12、mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。
因此数据库默认排序可以符合要求的情况下不要使用排序操作,尽量不要包含多个列的排序,如果需要最好给这些列建复合索引。
13、order by 索引 ,不起作用的问题(除了主键索引之外):
1、 如果select 只查询索引字段,order by 索引字段会用到索引,要不然就是全表排列;
2、如果有where 条件,比如where vtype=1 order by vtype asc . 这样order by 也会用到索引!
问题1:mysql索引类型normal,unique,full text的区别是什么?
normal:表示普通索引
unique:表示唯一的,不允许重复的索引,如果该字段信息保证不会重复例如身份证号用作索引时,可设置为unique
full textl: 表示 全文搜索的索引。 FULLTEXT 用于搜索很长一篇文章的时候,效果最好。用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。
SPATIAL 空间索引 空间索引是对空间数据类型的字段建立的索引
在实际操作过程中,应该选取表中哪些字段作为索引?
1.选择唯一性索引
2.为经常需要排序、分组和联合操作的字段建立索引
3.为常作为查询条件的字段建立索引
4.限制索引的数目
5.尽量使用数据量少的索引
6.尽量使用前缀来索引
7.删除不再使用或者很少使用的索引
MySQL目前主要有以下几种索引方法:B-Tree,Hash。
一、B-Tree
B-Tree是最常见的索引类型,所有值(被索引的列)都是排过序的,每个叶节点到跟节点距离相等。
所以B-Tree适合用来查找某一范围内的数据,而且可以直接支持数据排序(ORDER BY)
B-Tree在MyISAM里的形式和Innodb稍有不同:
MyISAM表数据文件和索引文件是分离的,索引文件仅保存数据记录的磁盘地址
InnoDB表数据文件本身就是主索引,叶节点data域保存了完整的数据记录
二、Hash索引
1.仅支持"=","IN"和"<=>"精确查询,不能使用范围查询:
由于Hash索引比较的是进行Hash运算之后的Hash值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的Hash算法处理之后的Hash
2.不支持排序:
由于Hash索引中存放的是经过Hash计算之后的Hash值,而且Hash值的大小关系并不一定和Hash运算前的键值完全一样,
所以数据库无法利用索引的数据来避免任何排序运算
3.在任何时候都不能避免表扫描:
由于Hash索引比较的是进行Hash运算之后的Hash值,所以即使取满足某个Hash键值的数据的记录条数,
也无法从Hash索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果
4.检索效率高,索引的检索可以一次定位,不像B-Tree索引需要从根节点到枝节点,
最后才能访问到页节点这样多次的IO访问,所以Hash索引的查询效率要远高于B-Tree索引
5.只有Memory引擎支持显式的Hash索引,但是它的Hash是nonunique的,冲突太多时也会影响查找性能。
Memory引擎默认的索引类型即是Hash索引,虽然它也支持B-Tree索引
mysql 中的$和# 使用方法的区别
1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111", 如果传入的值是id,则解析成的sql为order by "id".
2. $将传入的数据直接显示生成在sql中。如:order by $user_id$,如果传入的值是111,那么解析成sql时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id.
3. #方式能够很大程度防止sql注入。
4.$方式无法防止Sql注入。
5.$方式一般用于传入数据库对象,例如传入表名.
6.一般能用#的就别用$.
MyBatis排序时使用order by 动态参数时需要注意,用$而不是#
字符串替换
默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以它为背景设置安全的值(比如?)。这样做很安全,很迅速也是首选做法,有时你只是想直接在SQL语句中插入一个不改变的字符串。
比如,像ORDER BY,你可以这样来使用:
ORDER BY ${columnName}
这里MyBatis不会修改或转义字符串。
问:ioc容器,里面存储什么
Spring中依赖注入有三种注入方式:
一、构造器注入;
二、(setter方式注入);
三、(注解方式注入)。
问:CPU过高问题定位:
内存问题定位
使用jstat -gcutil命令查看进程的内存情况
使用jstack命令查看进程的堆栈情况 jstack 14063
hash表的存储原理:
我们知道,hash表存储利用到了数组以及链表,当键值对数据传入时,系统先将key值取出,利用hash函数转换成hash值,再运用散列法(此处用除法散列法取余),得到需要存入数组的下标index;
得到数组下标后,我们可以将key-value一起存入到数组中。
当使用index进行存储键值对的时候,如果此下标已经有了数据,那么将通过equals方法比较两个hash值是否相同,如果相等,再比较两个键值对key是否相等,如果不等,则在此下标位置,以链表的方式,将新存储的数据放到表头;如果相等,将覆盖原先的value;
hash表的查询原理:
同存储时一样,先将key值通过hash函数转换成指向内存地址的hash值;
通过equals方法,比较hash值查找对应的内存地址,然后再通过内存地址找到对应的链表,此时再通过equals方法,比较key值是否相等,若相等,取出键值对返回数据,如果不等,沿着链表继续向下寻找比较。
总结:
hash表就是通过传入的键值对,通过hash算法指向一个连续的存储空间(数组存储),将键值对存入数组;对于指向相同的存储空间的hash值,再以链表方式存储;这样hashmap不仅具有了数据查询快速的特性,同时有了链表方便插入、删除的特性;因此hashMap对于数据的存储查询具有非常好的特性;
问:hashmap为什么会造成死链
ConcurrentHashMap 1.7与1.8的区别总结:
1.锁结构不同
在JDK1.7中,ConcurrentHashMap基于Segment+HashEntry数组实现的。Segment是Reentrant的子类,而其内部也维护了一个Entry数组,这个Entry数组和HashMap中的Entry数组是一样的。所以说Segment其实是一个锁,可以锁住一段哈希表结构,而ConcurrentHashMap中维护了一个Segment数组,所以是基于分段锁实现的。 而JDK1.8中,ConcurrentHashMap摒弃了Segment,而是采用synchronized+CAS+红黑树来实现的。锁的粒度也从段锁缩小为结点锁
2.put()的执行流程有所不同
JDK1.7中,ConcurrentHashMap要进行两次定位,先对Segment进行定位,再对其内部的数组下标进行定位。定位之后会采用自旋锁+锁膨胀的机制进行加锁,也就是自旋获取锁,当自旋次数超过64时,会发生膨胀,直接陷入阻塞状态,等待唤醒。并且在整个put操作期间都持有锁。
而在JDK1.8中只需要一次定位,并且采用CAS+synchronized的机制。如果对应下标处没有结点,说明没有发生哈希冲突,此时直接通过CAS进行插入,若成功,直接返回。若失败,则使用synchronized进行加锁插入。
3.计算size的方法不一样
1.7:采用类似于乐观锁的机制,先是不加锁直接进行统计,最多执行三次,如果前后两次计算的结果一样,则直接返回。若超过了三次,则对每一个Segment进行加锁后再统计。
1.8:会维护一个baseCount属性用来记录结点数量,每次进行put操作之后都会CAS自增baseCount
4.引入了红黑树
这点和HashMap相同,引入了红黑树结构用降低哈希冲突严重的场景的时间复杂度。
问:从 url 输入到返回请求的过程
1.进行 url 解析编码,根据 dns 系统进行 ip 查找。(因为url只能是数字和字母,特殊字符可能会发生歧义;
比如http:www.baidu.com?key=value
,假如我的key
本身就包括等于=
符号,比如ke=y=value
,就会出现歧义)
3.次握手之后接着说道,建立完链接,就该请求html文件了,如果html文件在缓存里面浏览器直接返回,如果没有,就去后台拿
4.返回html之后,会解析html,然后布局和绘制
对线程池的理解
池化思想:线程池,字符常量池,数据库连接池
正常流程: 创建线程 -->> 使用线程 -->> 释放线程
为了提高资源利用率,线程池的有点:
1.提高线程利用率
2.提高程序响应速度
3.便于统一管理线程对象
4.可以控制最大并发数
(线程进来使用基础核心线程,不够就队列等待,等待队列满了就创建新线程,达到最大线程数量后使用拒绝策略,并发数量下来后,根据线程存活时间关闭空闲线程)
线程池参数的7个参数:
corePoolSize 线程池核心线程大小
maximumPoolSize 线程池最大线程数量
keepAliveTime 空闲线程存活时间
unit 空闲线程存活时间单位
workQueue 工作队列----无界队列有哪些,有界队列
threadFactory 线程工厂 一般使用默认的,使用场景:线程重命名,设置守护线程,设置优先级
handler 拒绝策略---4种拒绝策略 一般使用默认的丢弃任务,抛出异常