黑马面试.docx
《黑马面试.docx》由会员分享,可在线阅读,更多相关《黑马面试.docx(22页珍藏版)》请在冰豆网上搜索。
黑马面试
|--排版顺序(大家用editplus打开看,这个看txt文档是非常好的,Word文档不个人认为不好)
|----7K面试题
|----前5天的一些小知识点(面试时有人被考过写数组循环的)
|----面相对象的理解(4特性:
封装,继承,多态,抽象)
|----异常(异常机制)
|----多线程的理解(创建方式和线程重点内容)(sleep和wait区别:
释放锁上和怎么样醒自己总结)
|----集合(能说出整个体系)
|----IO流(写代码多用缓冲区和转换流吧)
|----正则表达式(正则的一些功能)
|----泛型(泛型的理解)
|----JDK1.5新特性
|----反射(说出反射的功能和作用)
|----代理(动态代理和代理的区别,动态代理三种创建方式)
|----线程池
|----类加载器(定义和创建过程)
|----归纳的一些面试被问的零散问题
|----面试时还被问到eclipse的快捷键(哥一口气说了十几个,这个始料未及)
(我由于说了是在传智播客毕业的,又自己复习了两月,老师基础就只问了我线程,其他全问的高新技术,高新技术全部都被涉及到了)
1.7K
1.17k面试题---交通灯管理系统(自己的理解)
首先使用名词提炼法,汽车过马路,有汽车,马路,红绿灯,红绿灯的控制系统四个名词。
车通过红绿灯过马路,车只有开启和停下的功能,他过
马路这个动作不是他控制的,是通过马路来判断的,马路上面有车的集合,某个时刻,马路上少了一辆车,就判断说车过了红绿灯。
创建路的对象road,路
是代表的某个方向的名字,所以在创建对象时加入name参数表示是哪个方向上的路,当我们创建路的对象的时候,创建某个方位的路的对象,那条路上就应
该有产生车和车过马路的共性行为,所以要创建两个线程来分别操作产生车和定时器来移除车,这里使用到Executors类,能产生单线程和控制器,控制器
通过判断这条路当前的灯是否为绿来让车同行,并确定同行的实现。
再来说红绿灯这个类,我们可以用枚举来实现,因为有12条路,就应该有12个灯,对象是固定取值的,由于当前路上的灯亮时,对面路上的灯也会
亮,当前路上灯为红时,下一路口上的灯为绿,所以我们可以创建一个有参的构造,参数分别为下一个路口的灯,对面路口的灯和当前灯的明暗情况,由于
在调用时下面的对象还未创建,所以参数用字符串表示。
灯有亮和不亮这两个属性。
还需要定义一个控制系统,设置成单例模式,来构造第一个为绿的灯,启动一个定时器,启动一个定时器,每隔10秒将当前灯变红和将下一个灯变
绿。
主函数创建路的对象,把路的名字作为参数循环穿进去,开启控制系统。
1.27k面试题---银行业务系统(这个是抄的张老师的PPT,自己没时间总结)
首先明确对象,售票窗口,号码产生机器,号码管理机器
号码产生器只有一个对象,所以采用单例,定义三个成员变量分别指向三个NumberManager对象,分别表示普通、快速和VIP客户的号码管理器,定
义三个对应的方法来返回这三个NumberManager对象。
号码管理器定义一个用于存储上一个客户号码的成员变量和用于存储所有等待服务的客户号码的队列集合。
定义一个产生新号码的方法和获取马上要为之服务的号码的方法,这两个方法被不同的线程操作了相同的数据,所以,要进行同步
售票窗口,根据服务窗口的类别分别循环调用三个不同的方法所以开启一个线程,使用Executors类。
线程里面根据不同的客户来执行售票方法,
所以号码的类型需要使用枚举,定义三种类型的对象,如果是快速窗口,首先获得快速号码管理器对象,得到马上要服务的号码,如果没有号码产生,则执
行普通窗口的方法
主函数里面用for循环创建出4个普通窗口,再创建出1个快速窗口和一个VIP窗口。
接着再创建三个定时器,分别定时去创建新的普通客户号码、新
的快速客户号码、新的VIP客户号码
2.面相对象的理解(我自己的理解,不一定准确)
面相对象基于面相过程,面相过程也是解决问题的一样思想,当我们需要解决一个问题时,我们需要设定好怎么一步步的去解决它,而具体的每
一步都需要我们自己亲自去完成,面对的是具体的每一个功能函数,功能相互协调完成需求。
随着需求的更改和方法的增多,发现自己去实现每一个的功能
已近忙不过来了,所以我们就想到把这些方法都封装起来,当我们要解决这一类问题的时候,只需要找到封装这一类方法的实例,问题就很轻松了,我们就
会发现我们面对的是一个个的封装某一类功能的实例,这个实例就是对象,面向对象是解决问题的一种思维模式,将复杂的问题简单化。
举例:
!
比如说想找朋友聊天,按照面向过程来理解就是找到朋友,让后面对面交流!
用面向对象来理解就是把找到朋友这个过程封装起来,我们不
用管这个过程,利用一个对象来实现跟朋友聊天的功能,这个对象就是手机。
面相对象中我们更多的是指挥者,使功能的实现更简单。
在实际开发中在怎么理解面相对象首先名词提炼法(由于任何对象都是Object类的子类,object就是物体的意思,就一名词),其次是说拥有数据,谁就
对外提供操作这些数据的方法。
面相对象有四个比较重要的特征(封装,继承,多态和抽象)
|----封装:
将现实世界中存在的某个客体的属性与行为绑定在一起,并放置在一个逻辑单元内。
(该逻辑单元负责将所描述的属性隐藏起来,外界
对客体内部属性的所有访问只能通过提供的用户接口实现)即将对象封装成一个高度自治和相对封闭的个体,对象状态(属性)由这个对象自己的行为
(方法)来读取和改变。
这样做既可以实现对客体属性的保护作用,又可以提高软件系统的可维护性。
只要用户接口不改变,任何封装体内部的改变都不会对
软件系统的其他部分造成影响。
结构化设计方法没有做到客体的整体封装,只是封装了各个功能模块,而每个功能模块可以随意地对没有保护能力客体属性实
施操作,并且由于描述属性的数据与行为被分割开来,所以一旦某个客体属性的表达方式发生了变化,或某个行为效果发生了改变,就有可能对整个系统产生
影响。
举例:
比如说手机打电话,发短信的操作系统都在手机内部封装起来了,外部只给我们按键来使用。
|----继承:
继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。
继承性是面向对象程序设计语言不同于其它语言的最重要
的特点,是其他语言所没有的,不过只能单继承,接口可以实现多继承所不能做的事,继承就比如说10年前的手机,那种大哥大,只能够打电话和发短信,现
在的手机继承了开始手机的打电话和发短信的功能,但是他又具备其他的特有功能,比如说聊QQ等等,这里引入OverLoad和Override,并且说明这两种方式都
是多态的体现
|----多态:
多态是指事物存在的多种体现形式,提高了代码的复用性,打个比方对于吃饭这件事.中国人是用筷子吃.外国人呢用刀和差子就是同
一件事,不同的对象做起来,产生的效果会不一样.父类的引用指向子类对象,不过只能操作父类特有的方法,不能操作子类特有的方法,但是多态的转型能够
满足这一需求,向上转型Fuf=newZi();向下转型Ziz1=(Zi)f;多态前提:
存在继承或者接口,即覆盖,当要使用子类特有功能时,就需要使用向下转
型,向下转型的好处:
可以使用子类特有功能。
弊端是:
需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。
在转换之前必
须做类型判断。
|----抽象:
抽象就是把一个对象分析出各个属性,来替代表达的手法。
抽就是抽离;象,表象。
表示出来的部分,编程上将对象抽象化是很有用的一个方法,能将枯燥的数据与单一对象对应起来,这样易于理解,也便于编程。
例如在编写学员管理系统。
学生的定义,首先要有名字,再有性别,再有学号,等等等等。
这些就是抽象出来的属性。
而在定义这些属性的时候就不要定义a="张三"b=1c=122222这些不容易辨识的属性名字。
而是用name="张三"sex="male"no="122223"这样一眼能认出来的名字。
这样不容易搞错,理解起来也不会困难。
(一接口为例,都是理解的,定义方面的内容自己写)接口就是类里面共性方法的抽取,在实际开发中只要知道了类实现的接口的功能,就
能大致猜测这个类的做什么的,比如说手机它实现一个接口能打电话和发短信,我们只要得到这个接口就能够知道这个类是用来描述一个手机的,在我们查看
API的时候,接口也往往在最前面,在开发中,尽量使用接口来解决某一类的问题,这样会使方法看的简单,当方法多了时继承会使代码显得笨重。
instanceof运算符可以用来决定某对象的类是否实现了接口
2.2抽象类和接口的区别(自己补)
接口和抽象类的概念不一样。
接口是对动作的抽象,抽象类是对根源的抽象。
抽象类表示的是,这个对象是什么。
接口表示的是,这个对象能做什么。
比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。
说明,他们都是人。
人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它.
所以,在高级语言上,一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)。
第一点. 接口是抽象类的变体,接口中所有的方法都是抽象的。
而抽象类中不一定都是抽象方法。
第二点. 接口可以继承,抽象类不行
第三点. 接口定义方法,不能实现,而抽象类可以实现部分方法。
第四点. 接口中基本数据类型为static而抽类象不是的。
当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
抽象类的功能要远超过接口,但是,定义抽象类的代价高。
因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。
在这个类中,你必须继承或编写出其所有子类的
所有共性。
虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。
而且你可以在一个类中同时实现多个接口。
在设计阶段会降低难度的。
3.异常:
运行时期出现的问题(其他自己补,没什么重点)
try-catch-finally实现方式
|---try执行的代码,其中可能有异常。
一旦发现异常,则立即跳到catch执行。
否则不会执行catch里面的内容
|---catch除非try里面执行代码发生了异常,否则这里的代码不会执行,抛出异常对象
|---finally不管什么情况都会执行,包括trycatch里面用了return,可以理解为只要执行了try或者catch,就一定会执行finally
注意:
如果没有catch语句块,那么finally块就是必须的。
4.多线程的理解
|---什么是进程:
当我们运行硬盘上的某个应用程序的时候,操作系统会把这个应用程序从硬盘上加载到内存中,然后在内存中为这个应用程序
开辟运行自己程序的内存空间。
这个内存空间就称为内存中的一个进程。
|---什么是线程:
在一个进程中,负责独立运行某个功能,或者某些代码块的那个小单元,就称为当前进程中一个线程。
我们的JVM就是一个多线
程的应用程序。
线程是用来负责进程中某个独立的功能,或者某段代码块的执行。
在执行的过程中,它有自己独立的运行空间,并且多个线程执行不会相互
影响。
如果一个CPU只有一个处理核心,那么在某个时刻真正只能去处理一个线程。
如果CPU拥有多个处理核心,那么在某个时刻就可以实现多个程序同时执
行了我们创建多线程的目的是让多部分代码能够同时执行
|---创建线程的两种方式:
1,继承Thread类,复写run方法,Thread是线程类,没什么好说的
2,由于类的单继承性的局限性,有些类已近继承了其他类,如果它还想实现多继承,就不能继承Thread类了,那么它
就只能实现Runnable接口,该接口抽取了Thread类里面的run方法,这样做的目的是让Thread类就专门用来负责线程的各种操作,让Runnable接口专门用来
封装线程要执行的任务。
将Runnable实现类的对象作为参数传递到Thread类构造里面,是为了关联线程任务和线程对象。
|---多线程的几种状态:
临时阻塞状态(具备CPU执行资格,不具备执行权),运行状态(线程具备执行资格和执行权),冻结状态(线程不具备CPU执
行资格,线程释放CPU执行权)
|---多线程里面的重点内容:
(面试有人被问过)
当多线程执行共享数据时,CPU会在线程间不停切换,这种切换我们是不能控制的,当线程在执行共享数据过程中CPU
是可能切换到其他线程,所以线程就会出现完全问题,这时我们需要使用到同步代码块synchronized。
当线程进入到同步代码块里面时,需要获取一把锁,
OBJECT对象都行,其他线程会处于临时阻塞状态,知道该线程把共享数据操作完成才能执行其他数据。
还有同步函数,同步函数操作的锁为this(调用这个
方法的对象),静态同步方法的锁为类名.class
死锁:
最常见的原因是锁的嵌套循环,例如ifelse判断语句里面,当线程满足if条件,获得A锁执行if里面的同步,A同步里面还嵌套
了一个同步,需要B锁,这时线程切换到else条件,获得了B锁执行里面的同步,这个同步里面也嵌套了一个同步,需要获得A锁才能执行。
sleep是线程冷却,比喻说释放魔法技能有技能CD。
5集合
(说出整个体系,我自己总结的,不定正确,发现问题自己改)(ArrayList初始容量为10,每次50%延长,Vector每次100%延长,浪费空间)
集合是存储对象最常用的一种方式,集合层次中的根接口是Collection,它有两个子接口List和Set,List里面的元素是有序的,集合有索引,所以
元素可以重复,Set集合元素不能重复,无序无索引。
集合相当于一个容器,由于每一个容器对数据的存储方式不同,List集合可分为ArrayList,LinkedList
vector,Vector是同步的,底层是数组结构,他在java1.0版本就存在,增删速度都慢,而且集合里面的方法都很长所以在java1.2版本后就被ArrayList取代,
集合ArrayList底层也是数组结构,查询速度快,增删速度慢,应为他每次增删后面的位置都会发生变化,LinkedList底层是链表结构,增删速度快,查询速度超慢,增删只需要把元素之间的链子解开,加入新元素即可。
Set集合分为HashSet和TreeSet,HashSet底层是哈希表,哈希表底层使用的也是数组机
制HashSet里面保证对象的唯一性是通过元素的两个方法HashCode和equals,哈希表底层使用的也是数组机制,数组中也存放对象,而这些对象往数组中存放
时的位置比较特殊,当需要把这些对象给数组中存放时,那么会根据这些对象的特有数据结合相应的算法,算出这个对象在数组中的位置,然后把这个对象存
放在数组中。
而这样的数组就称为哈希数组,即就是哈希表。
当给哈希表中存放元素时,需要根据元素的特有数据结合相应的算法,这个算法其实就是
Object中的hashCode方法。
由于任何对象都是Object类的子类,所以任何对象有拥有这个方法。
即就是在给哈希表中存放对象时,会调用对象的hashCode方法
,算出对象在表中的存放位置,这里需要注意,如果两个对象hashCode方法算出结果一样,这样现象称为哈希冲突,这时会调用对象的equals方法,比较这两
个对象是不是同一个对象,如果equals方法返回的是true,那么就不会把第二个对象存放在哈希表中,如果返回的是false,就会把这个值存放在哈希表中。
总结:
保证元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。
如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode
和equals方法建立属于当前对象的比较方式。
TreeSet的底层是二叉树结构,往TreeSet里面存对象的时候,他会进行排序,如果存入的对象没有比较的功能,
则必须实现一个comparable接口,覆盖compareTo方法,这种方式也叫元素的自然顺序,也叫默认顺序。
当元素不具备比较性或者具备的比较性不是所需要的,
这时候就必须让集合自身具备比较性,进入集合的元素被集合比较,,在集合初始化时就具备了比较方式,定义一个比较器,将比较器对象作为参数传递给
TreeSet的构造函数TreeSet(比较器儿子对象),Comparator接口比较器(就两方法compareequals),当两种比较方式都存在时,以比较器为主,HashMap集
合存储的是一定对应关系的两个对象,底层也是哈希表,存放的数据由key和value组成,key不能重复,value可以重复,由于Map里面没有迭代器,只有获得
Set集合才能迭代。
第一种方式
HashMapmap=newHashMap();
map.put("zhangsan","上海");
map.put("sili","上海");
map.put("wangwu","上海");
Setset=map.keySet<>();keySet获取map集合中的所有key值,组成一个Set集合
for(Iterator()it=set.iterator<>();it.hasNext();){
Stringkey=it.next();
Stringvalue=map.get(key);get方法,通过key值找到对应的value值。
sop(key+value);
第二种方式(有人面试被问过用泛型写出)
Set>entrySet=map.entrySet();entrySet把每一对封装成对象
for(Iterator>)it=entry.iterator;it.hasNext;){
Entryentry=it.next;
Stringkey=entry.getKey();
Stringvalue=entry.getValue();
Map有哪些常用类,各有什么特点?
Map集合下有HashMap、HashTable、TreeMap三个子接口。
HashTable:
底层数据结构是哈希表,不可以存入空健和空值。
线程同步。
基于老的,被HashMap取代
HashMap底层的数据结构也是哈希表。
可以存入空健空值。
线程非同步。
取代了HashTable(基于老的类)
TreeMap底层的数据结构是二叉树。
线程非同步,也可以用于给Map集合中的健进行排序。
6.IO流(这个多写几遍,蛮好理解的)
(我笔试复制MP3(字节缓冲流)和复制文件夹里的文件(递归?
什么是递归?
直接递归和间接递归?
))
直接递归调用就是在函数a(或过程)中直接引用(调用)函数a本身
间接递归调用就是在函数a(或过程)中调用另外一个函数b,而该函数b又引用(调用)了函数a
流是用来操作数据的,而数据在硬盘或者内存里面是以二进制数来存在的,8个二进制表示一个字节,用字节来操作流,字节流分输入字节流
InputStream和输出字节流OutputStream,缓冲区BufferInputStream和BufferedOutputStream(存入字节一下子读出或写入)
使用字节流缓冲区来复制字节文件(字节流也可以)
BufferInputStreambis=newBufferedInputStream(newFileInputStream("C:
a.mp3"))
数据最终是保存在文件中,文件分为视音频文件,图片文件,文本文件,文本文件我们接触的机会太多,就需要学会使用操作更方便的字符流
当不是默认的GBK编码表来读出或者写入文件时就必须使用转换流
BufferedReaderbr=newBufferedReader(newBufferedReader(newFileInputStream("C:
\\a.mp3")),"UTF-8")
一般转换流和缓冲流效率更高,字符流缓冲区的readLine和newLine写两遍
7正则表达式(面试呗问到)
|---用一些符号来操作字符串,书写方便,阅读星太差(里面的符号啥的,基本的几个认得就可以)
|---功能:
1,匹配:
使用matchs方法(具体方法代码自己看)
2,切割:
split
3,获取功能:
find
4,替换功能:
replaceAll
8反射:
(所有内容都会被问)
|---定义:
就是把java类中的各种成分映射成相应的java类,说白了就是获得一个类里面的成员
|---获取字节码的方法:
1,类名.class(System.class)2,对象.getClass()3,Class.forName("类名比如java.util.Data")
|---反射是为了实现(代码自己看):
1,在运行时判断任意一个对象所属的类。
2,在运行时构造任意一个类的对象。
3,在运行时判断任意一个类所具有的成员变量和方法。
4,在运行时调用任意一个对象的方法
|---反射与内省区别?
反射可以操作各种不同的java类,那么内省只是通过反射来操作JavaBean的。
JavaBean类里面操作的都是成员变量,都是通过setXXX和getXXX方法
来获取成员变量,这样的类用内省来操作会更简单。
|---反射及暴力反射,获得私有方法代码(这里用到反射)
暴力反射:
类里面的变量私有了,通过普通的getField反射方法无法获得,只能通过getDeclaredField()获得
代码:
packageday_7_15;
importjava.lang.reflect.Field;
classReflectPoint{
privateintx,y;
ReflectPoint(intx,inty){
this.x=x;
this.y=y;
}
}
publicclassViolenceReflectDemo{
publicstaticvoidmain(String[]args)throwsException,Exception{
ReflectPointpoint=newReflectPoint(3,5);
//field类代表某个类中的成员变量,这里是私有变量只能通过暴力反射的方法获得,getDeclaredField()方法
Fieldfield=point.getClass().getDeclaredField("y");//得到了方法看不见方法,需要判断
//通过方法看见了,但是拿不到
field.setAccessible(true);
//y这个还有变量y的变量类对象来获取对象里的y,拿到了变量
System.out.println(field.get(point));
}
}
9代理(面试时全问了)
|---定义:
代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式的思想是为了提供额外的处理或者不