目录
- 第一幕 、
- 第一场)某大厦楼下大门前
- 第二场)电梯中
- 第三场)走廊中
- 第二幕、
- 第一场)公司前台
- 第二场)公司卫生间
- 第三幕、
- 第一场)一场异常面试
- 第四幕 、
- 第一场)大厦楼下门口
- 第二场)大门口
- 第五幕 、
- 第一场)街边
友情提醒
背面试题很枯燥,加入一些戏剧场景故事人物来加深记忆。PS:点击文章目录可直接跳转到文章指定位置。 |
第一幕 、
第一场)某大厦楼下大门前
【门卫甲,门卫乙,面试者老王,路人等】
门卫甲:来者何人?报上名来。
老王:隔壁老王前来面试
门卫乙:现在面试Java的人很多,如果谁都放进去,恐怕总裁会责怪我们,你先说说1.Java的跨平台原理
,回答对了再进去面试吧。
老王:这个公司倒是有意思,门卫当起了面试官。Java实现跨平台是JVM(Java虚拟机)起的作用,编译好了一份Java字节码,换到不同的平台上时,并不需要重新编译,前提是这些平台上都安装了相应平台的JVM,JVM不是跨平台的。
门卫甲:我也有个问题,2.说一下Java的垃圾回收机制
,对了我去买个冰棍,老乙给你也带一根。
老王:给我也带一根钟薛高我就给你说垃圾回收机制;
垃圾回收器通常是作为一个单独的低级别的线程运行,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。垃圾回收器不可以马上回收内存,可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。 回收机制包括:
①分代复制垃圾回收
不同生命周期的对象可以采取不同的收集方式,生命周期比较长的如Http请求中的 Session对象、线程、Socket连接。生命周期比较短的如:String对象。分代回收是把不同生命周期的对象放在不同代上,包括年轻代、年老代,和持久代,因此垃圾回收区域、时间也不一样。
②标记垃圾回收
使用标记清除算法,内存耗尽时,程序将会被挂起,垃圾回收开始执行.当所有的未引用对象被清理完毕时,程序才会继续执行。标记清除算法由两个阶段组成:
标记阶段,标记所有的可访问对象。
收集阶段,垃圾收集算法扫描堆并回收所有的未标记对象。
③增量垃圾回收
解决标记清除的长停顿问题。增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾。这会造成较小的应用程序中断。
门卫乙:其实我也不知道你说的对不对,不过你照着手机读是不是有点过分了。手机收起来问你一个简单的。3.JVM加载class文件的原理机制?
老王:不知道
门卫乙:让你手机收起,你就不知道是吧,我来告诉你经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件,JVM会确保这个类被加载、连接(验证、准备和解析)和初始化。
①类的加载:是由类加载器和它的子类在运行时查找和装入类文件中的类,把.class文件中的数据读入到内存中。
②连接阶段:加载完成后,进入连接阶段,包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。
③最后JVM对类进行初始化,包括:如果类有父类先初始化父类,类中存在初始化语句,就依次执行这些初始化语句
门卫乙:我话说完,你赞成还是反对。
老王:赞成。一直都是你问我,我也问你一个:4.门卫大哥,你对Java内存了解多少?
门卫乙:一般我不轻易显露自己的学识,但是你既然诚心诚意的发问了我就露一手:
①Java内存分为堆与栈,java中一个线程就会相应有一个线程栈与之对应,Main函数就是栈的起始点,也是程序的起始点即程序执行的入口。
②栈是运行时的单位,存储基本数据类型和堆中对象的引用以及当前程序相关的信息(局部变量、程序运行状态、方法、方法返回值)等, 而堆是存储的单元,存储对象信息如对象的属性。
③栈解决程序的运行问题,即程序如何执行(运行逻辑),或者说如何处理数据,堆解决的是数据存储的问题,即数据怎么放,放在哪儿。
④堆与栈的分离,使得堆中的内容可以被多个栈共享,提供了有效的数据交互方式,节省了空间。
⑤由于栈只能向上增长,就会限制住栈存储内容的能力,而堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。
门卫甲:你回来了。
门卫乙:是我回来了,不是你回来了。在上班时间能吃冰棍吗?老王你说说5.为什么不把基本类型放在堆中
门卫甲:抢答:因为基本类型占用的空间一般是1~8个字节,不会出现动态增长的情况—长度固定,因此栈中存储就够了,如果把它存在堆中是没有什么意义的
门卫乙;问你了吗,显眼包。老王回答一下6. java中的参数传递是传值呢?还是传引用?
门卫甲: 抢答: 对象传递是引用值传递(传递的是引用的地址值),基本数据类型传递是值传递
老王:我可以进去了吗?过度的饱食有伤胃口,毫无节制的门卫面试,结果只会让我错过面试的时间。— —对了把冰棍给我。
门卫甲/乙:要糕没有,要进去你就进去吧。
第二场)电梯中
【老王,保洁公司老板,保洁阿姨】
老板:关于公司保洁方面你已经做的很好了。但是其他种种事项还需要阿姨你单独来办公室单独讨论拟定。我知道你在这方面的阅历尚浅,先问你一个问题:7.你对面向对象思想的理解
保洁阿姨:想炒鱿鱼直接说,何必整这出。亏我昨天背了面试题:面向对象编程简称OOP技术,使用许多代码模块,每个模块都只提供特定的功能,它们是彼此独立的,可以增加代码重用的几率,更加有利于软件的开发、维护和升级。另外OOP的三大核心特性:继承、封装、多态的特性,使得在面对象编上能够设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展,而且成本较低,所以这一编程思想是目前一种应用最为普遍的软件设计思想。
老板:看来你还是有点功底:8.说说面向对象的三大特性
老王:我抢答:
①封装
封装就是把描述一个对象的属性和行为的代码封装在一个“模块”中,也就是一个类中,属性用变量定义,行为用方法进行定义,方法可以直接访问同一个对象中的属性。
②继承
继承是子类共享父类数据和方法的机制,这是类之间的一种关系,提高了软件的可重用性和可扩展性。在定义或实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并可以加入新的内容,或修改原来的方法使之更适合特殊的需要,这就是继承。
③多态
多态指的同一个对象,在程序不同时刻的多种运行状态。指的就是父类引用指向子类对象,调用方法时会调用子类的实现而不是父类的实现。不同子类继承了同一个父类。当我们统一用父类引用变量去调用这些不同的子类对象中的方法时,我们称为多态。
为使多态能运行,存在着继承或者实现关系
可以将子类的对象赋给父类/接口,以多态的形式来传递参数,增强了参数类型的灵活性。
多态的好处和弊端:
好处:多态的存在提高了程序的扩展性和后期可维护性
弊端:虽然可以预先使用,但是只能访问父类中已有的功能,运行的是后期子类的功能内容。不能预先使用子类中定义的特有功能。
多态应用场景:当接口已经确定,但同一个接口在不同环境需要不同实现的时候。如:工厂模式
老板:你是谁,还抢答,不过看起来答得不错,来保洁公司上班吧。
老王:口出狂言。
保洁阿姨:老板我要辞职去面试Java。
第三场)走廊中
【保洁阿姨,老王】
保洁阿姨:“对潇潇暮雨洒江天,一番洗清秋。渐霜风凄紧,关河冷落,残照当楼。是处红衰翠减,苒苒物华休。惟有长江水,无语东流。不忍登高临远,望故乡渺邈,归思难收。叹年来踪迹,何事苦淹留。想佳人、妆楼颙望,误几回、天际识归舟。争知我、倚栏杆处,正恁凝愁。”请问:9.有几种方法倒序输出这首词
老王:这话题过弯速度太快了吧。我百度了一下有四种方法:
①利用String类的toCharArray(),再倒序输出数组的方法。
/** 利用String类的toCharArray(),再倒序输出数组的方法*/private static void reverseString1(String str) {char[] chr = str.toCharArray();for (int i = chr.length-1 ; i >= 0; i--) {System.out.print(chr[i]);}System.out.println("\t");}
②利用String类提供的subString()方法,利用递归的办法输出倒序字符串。
/** 利用String类提供的subString()方法,利用递归的办法输出倒序字符串。*/private static void reverseString2(String str) {if (str.length() == 1)System.out.println(str);else {String subString1 = str.substring(0, str.length() - 1);String subString2 = str.substring(str.length() - 1);System.out.print(subString2);reverseString2(subString1);}}
③定义一个StringBuffer类,用StringBuffer类中的reverse()方法直接倒序字符串。
/** string倒序输出 * 定义成一个StringBuffer类,用StringBuffer类中的reverse()方法直接倒序字符串。* 2018-5-18 13:22:10*/private static void reverseString3(String str) {StringBuffer buffer = new StringBuffer(str);System.out.println(buffer.reverse());}
④利用字符串串联符号"+"特性
public class MyTest {public static void main(String[] args) {String s = "对潇潇暮雨洒江天";String s2 = "";for (int i = s.length() - 1; i >= 0; i--) {s2 += s.charAt(i);}System.out.println(s2);}}
}
保洁阿姨:你有对象吗?我看你Java基础知识很扎实啊。不如介绍一下10.Java对象初始化顺序
老王:有那么一秒我认为我要脱单了。Java对象初始化顺序如下:
①第一种是本类的初始化
静态变量、静态初始化块、变量、初始化块、构造函数
②第二种是含有父类的初始化顺序。
父类静态变量、父类静态初始化块、子类静态变量、子类静态初始块、父类变量、父类初始化块、父类构造函数、子类变量、子类初始化块、子类构造函数。
老王:礼尚往来我也问你一个问题吧。Java类都有一个共同的祖先类,就是Object,请问11.Object中有哪些方法:
保洁阿姨:我当然知道:
protected Object clone() 创建并返回此对象的一个副本。
boolean equals(Object obj) 指示其他某个对象是否与此对象“相等”。
Class<?> getClass() 返回此 Object 的运行时类。
int hashCode() 返回该对象的哈希码值。
void notify() 唤醒在此对象监视器上等待的单个线程。
void notifyAll() 唤醒在此对象监视器上等待的所有线程。
String toString() 返回该对象的字符串表示。
void wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
老王:12.方法重载和重写的区别?Overload的方法是否可以改变返回值的类型?
保洁阿姨:
Overload是重载,Override是覆盖,也就是重写。
①重载Overload
表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。是否可以改变返回值类型,在重载的定义中,与方法是什么类型返回值无关
②重写Override
表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给重写了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,子类方法的访问权限只能比父类的更大,不能更小。
保洁阿姨:面试的真谛在于:那就是认清了面试的真相后,依然热爱面试。发觉了工作的残酷后,依然过着美丽人生。祝你好运。
第二幕、
第一场)公司前台
【接待人员埃斯卡莱罗,面试者老王,面试者奥斯卡】
奥斯卡:老王这个公司不好,你千万不要来面试,他们整天加班没有加班费,放假只能调休。
埃斯卡莱罗:老王别理他,他也是来面试的。你先填一下表格:1.Java基础语法面试题
问题 | 答案 |
---|---|
".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? | 可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。一个文件中可以只有非public类,如果只有一个非public类,此类可以跟文件名不同 |
&和&&的区别 | &和&&都可以用作逻辑与的运算符,&&为短路与,另外&可以做为整数的位运算符,&&的两侧必须要求是布尔值或者布尔表达式。如果左侧为false程序不会去继续判断右侧表达式的真假。 |
java中的基本数据类型有哪些 | byte(位)1字节、short(短整数)2字节、int(整数)4字节、long(长整数)8字节、float(单精度)4字节、double(双精度)8字节、char(字符)2字节,boolean(布尔值)理论上占1bit。8bit是一字节。 |
Integer与int的区别 | int基本数据类型,Integer是java为int提供的封装类。int的默认值为0,而Integer的默认值为null,int是值传递值保存在值栈中,integer是引用地址传递,存在堆中通过对象的引用来调用。 |
char型变量中能不能存贮一个中文汉字 | char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中可以存储汉字。 |
7)short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错? | 由于s1+1运算时会自动提升表达式的类型,所以结果是int型,再赋值给short类型s1时,编译器会提示错误,需要强制转换类型。由于+=是java语言规定的运算符,Java编译器会对它进行特殊处理,因此可以正确编译。 |
"=="和equals方法有什么区别? | = =操作符专门用来比较两个变量的值是否相等,比较两个基本类型的数据或两个引用变量是否相等,只能用= =操作符。equals方法只能用于判断引用类型,重写后用于判断对象内容是否相同。不重写方法的话就是使用==操作符判断引用类型的地址值。所以一定要记得重写啊,不然白给 |
作用域public,private,protected,以及不写时的区别? | public:当前类,同包,子类,其他包。protected:当前类,同包,子类。default:当前类,同包。private:当前类。 |
Java 常用包 | java.lang–语言包:包括Object类、Thread类、String、Math、System、Runtime、Class、Exception、Process等java.util–实用工具包:Scanner、Date、Calendar、LinkedList、Hashtable、Stack、TreeSet等;java.NET–网络功能包:URL、Socket、ServerSocket等;java.sql–数据库连接包:实现JDBC的类库;java.io–输入输出包:与流相关的包; |
Java 常用接口 | Comparable ,Collection,Set, List, Map, Runnable Iterable Iterator 等 |
老王:奥斯卡,Java这么卷吗?保洁阿姨都来卷Java了,这也太抽象了。说到抽象:2.abstract class(抽象类)和interface(接口)有什么区别?
奥斯卡:
①抽象类:
含有abstract修饰符的class即为抽象类,abstract类不能创建实例对象。含有abstract方法的类必须定义为abstract class,有抽象方法和非抽象方法。abstract class类中定义抽象方法必须在具体子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。
②接口:
接口中的成员变量类型默认为public static final。在Java 8之前,接口只能定义抽象方法,所有的方法都必须由实现该接口的类来实现。但是,在Java 8中,接口可以定义默认方法和静态方法。
一个接口可以实现多个父接口,将实现的各个接口用逗号隔开
接口的特点:是对外暴露的规则,是功能的扩展,接口的出现降低耦合性。
埃斯卡莱罗:聊什么呢,安静点。知道什么是静态关键字吗:3.static关键字
奥斯卡:
①静态初始化块:static{ } :
使用static来修饰的自由块只在类加载的时候执行一次,通常用于初始化静态变量。它只执行一次,它比main还先执行,执行顺序是静态代码块–构造方法
②static关键字声明属性(类属性):
静态变量也称为类变量,可以直接通过类名调用。也可以通过对象名调用。这个变量属于类,可以被该类的所有实例对象所共享,成员变量也称为实例变量,只能通过对象名调用,这个变量属于对象。
③static关键字声明方法:
可以在不创建对象的情况下,直接使用类名.方法名()的形式调用,静态方法只能访问静态成员,如果想在静态方法中调用非静态方法需要先创建对象。
④静态的特点:
随着类的加载而加载,优先于对象存在,对所有对象共享,可以被类名直接调用
埃斯卡莱罗:4.abstract修饰的方法是否可同时是static或native或是synchronized?
奥斯卡:
不可以
①abstract与static不能同时使用
static关键字修饰的成员是属于类的,其可以直接被类所调用。而abstract修饰的方法为抽象方法为了让子类继承后重写该方法,即无方法体的方法,不能够被直接调用,需要在子类或实现类中去编写完整的方法处理逻辑后才能使用。
②native与abstract不能共用
表示该方法要用另外一种依赖平台的编程语言实现的,不存在着被子类实现的问题,所以,它也不能是抽象的,不能与abstract混用。
③synchronized和abstract不能共用
abstract方法只能存在于抽象类或接口中,它不能直接产生对象,而默认synchronized方法对当前对象加锁,没有对象是不能加锁。另外synchronized不能被继承,子类继承时,需要另加修改符。
老王:人生最终的价值在于觉醒和思考的能力,而不只在于面试。— —亚里士老王
第二场)公司卫生间
【如厕计时器(控制员工上厕所时间),面试者老王】
老王:出恭。
如厕计时器:你有30秒的时间摆脱你的沉重负担。超时将会扣钱。回答面试题可以延长时间。5.说一下单例(Singleton)设计模式
老王:今天算是长见识了。良心公司,强迫症福利,还有助学习进步。
单态设计模式创建一个类,应满足:
①构造函数私有化
②本类中创建一个该类私有对象
③为了方便其他程序访问到该类对象,可对外提供一个公共访问方式
Runtime类就是单例设计模式。
饿汉式:当类加载的时候,就创建对象。
class Single{private Single(){}//将构造函数私有化,不让别的类建立该类对象private static final Single s=new Single();//自己建立一个对象public static Single getInstance()//提供一个公共访问方式{return s;}}
懒汉式:类才加载进内存的时候,对象还没有存在,只有调用了 getInstance()方法时,对象才开始创建。
class Single{private Single(){} private static Single s;public static Single getInstance(){if(s==null)s=new Single();return s;}}
懒汉式是延迟加载,如果多个线程同时操作懒汉式时就有可能出现线程安全问题,可以加同步来解决。但是加了同步之后,每一次都要比较锁,效率就变慢了,所以可以加双重判断来提高程序效率
public static Single getInstance(){if(s==null){synchronized(Single.class){if(s==null) s=new Single();}}return s;}
如厕计时器:看来你已经明白了单例设计模式的意义,卫生间也是这样设计的噢,不允许有两个对象在同一个坑位。
老王:快点问下一个问题,没时间了。
如厕计时器:6.final关键字
老王:
①final类:不可被继承,如java.lang.Math就是一个 final类,不可被继承。
②final变量:在初始化后不可改变变量值,用于常量定义。如果final变量是引用变量,则不可以改变它的引用对象,但可以改变对象的属性。final修饰的变量是一个常量。只能被赋值一次。
③final方法:不可被重写
④final是引用变量不能重新赋值,但是引用指向的对象的内容可以变化
例1:有编译错误
final StringBuffer a=new StringBuffer("immutable");
a=new StringBuffer("");例2:正确final StringBuffer a=new StringBuffer("immutable");a.append(“123”);
如厕计时器:7.说一下内部类
老王:心很累:
①内部类:
定义在一个类内部的类(inner class),内部类的对象能访问它所处类的私有数据
内部类能隐藏起来不为同一个包中其他类访问,当内部类定义在外部类的成员位置,而且非私有,则可以在其他外部类中直接建立内部类对象
格式:外部类名.内部类名 变量名 = new 外部类对象.内部类对象
如:Outer.Inner in = new Outer().new Inner()
②匿名内部类
前提:继承一个类或者实现一个接口
好处:简化代码书写
弊端:不能直接调用自己的特有方法,不能执行强转换动作
格式:
new 父类名或者接口名()
{
重写父类方法或者实现接口中的方法。
也可以自定义其他方法。
};
如厕计时器:8.说一下String
老王:①字符串虽然是引用类型属于对象,但是它不是存储在堆空间中,而是存储在方法区中的字符串常量池中。
②String的底层是数组。jdk1.8及以前String使用的是char数组,jdk1.9及以后使用的是byte数组。
③字符串内容做= =比较,比较的是地址值。
④字符串与基本类型之间的转换
//类型转换:字符串转为基本类型String s1 = "123";int i = Integer.parseInt(s1);String s2 = "3.14";double d = Double.parseDouble(s2);//类型转换:基本类型 -> Stringint num = 123;String s5 = num + "";String s6 = Integer.toString(num);String s7 = String.valueOf(num);
⑤String类常用方法:将字符串内容转换为全大写/小写
String toLowerCase():将此 String 中的所有字符都转换为小写。 String toUpperCase():将此 String 中的所有字符都转换为大写。int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引。 int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。 int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引。 int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。 String substring(int beginIndex): 截取字符串从起始索引截取到最未String substring(int beginIndex, int endIndex): 截取字符串满足含起始索引不含结束索引String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。String concat(String str):将指定字符串连接到此字符串的结尾
如厕计时器:9.说一下StringBuffer与StringBuilder的区别
老王:
①String对象是不可变对象,指的是想要改变字符串值时会复用字符串常量池中的地址值。StringBuffer与StringBuilder则是可变的字符序列,能修改值。
②拼接字符串时,String会产生新对象,而StringBuffer只是增加新字符,不产生新对象,因此效率高。
③缓冲区就是一个临时空间,它里面可以临时存储数据。缓冲区本身就是一个容器,把需要修改的字符串先存储到字符串缓冲区容器中,在容器中修改完成后存储在字符串常量池中。
④StringBuffer不提供线程同步,StringBuilder是线程同步的,StringBuilder效率不如StringBuffer,但是StringBuilder 的方法不是线程安全的(不能同步访问)。由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下会使用 StringBuilder 类。
第三幕、
第一场)一场异常面试
【面试官老吉,面试官潘安,面试者老王】
老吉:今天有60场面试,从早上九点,到晚上六点。我们快点开始吧。1.Error、Exception区别
老王:你姓王吗?
①Error类和Exception类的父类都是throwable类
②Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。仅靠程序本身无法恢复和和预防,建议让程序终止。
③Exception类表示程序可以处理的异常,可以捕获且可能恢复。应该尽可能处理,而不应该随意终止异常。
王老吉:反正我不姓加。下一个问题你问吧,潘安。
潘安:程序出了问题还可以抛出异常,国家出了问题又能怎么办呢。西晋惠帝时期的八王之乱导致了五胡乱华,而我也在八王之乱中被杀了。所以:2.说一下Java中的异常处理机制的简单原理和应用。
老王:才三十二岁,你就有白头发了,Java程序员这一行不好干啊。
①异常是指java程序运行时(非编译)所发生的非正常情况或错误。异常处理机制是识别及响应错误的机制。
②Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象中,该对象中包含有异常的信息,我们也可以自定义异常类(继承Exception或RuntimeException)。所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error(一般不关注)和Exception(合理的应该处理的异常)。
③异常分为运行时异常和编译期异常
编译器强制编译期异常必须try…catch处理或用throws声明继续抛给上层调用方法处理,所以编译期异常也称为checked异常。
④而运行异常可以用try catch抓取也可以不处理,所以编译器不强制用try…catch处理或用throws声明。
潘安:3.常见的异常
老王:ArithmeticException(算术异常)
ClassCastException (类转换异常)
IllegalArgumentException (非法参数异常)
IndexOutOfBoundsException (下标越界异常)
NullPointerException (空指针异常)
老吉:4.说一下异常处理:throw,throws和try…catch…finally
老王:如果异常一直用throw抛出总不是个事情,异常总得有处理的时候这时候需要用到throws和try…catch…finally
/*格式: try{//可能出现异常的代码}catch(异常类型1 对象名){//逻辑代码}catch(异常类型2 对象名){//逻辑代码}*/
public class TestException_3 {public static void main(String[] args) {test2();}public static void test2() {/*运行时异常:数组下标越界异常java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10*/try {int[] arr = new int[10];System.out.println(arr[10]);} catch (Exception e) { //参数位置可能发生多态 万能捕获器 表现:Exception e = new ArrayIndexOutOfBoundsException();e.printStackTrace(); //打印堆栈信息 ==> 将异常对象所属的类型、原因、涉及的行数打印展示在控制台
// System.out.println(e.getMessage()); //得到异常出现的原因}System.out.println("执行了");}
}
try…catch…finally结构中,finlly被定义在最后位置,将一定需要被执行的代码定义在finally的内部,finally中的代码一定会被执行。
/*格式: try{//可能出现异常的代码}catch(异常类型1 对象名){//逻辑代码}catch(异常类型2 对象名){//逻辑代码}finally{//定义一定需要被执行的代码}*/
public class TestException_3 {public static void main(String[] args) {test4();}public static void test4() {FileInputStream fis = null;try {fis = new FileInputStream("hello2.txt");int len;while ((len = fis.read()) != -1) {System.out.println((char) len);}int num = 10 / 0;} catch (Exception e) {e.printStackTrace();}finally { //这里面的代码一定会执行try {if (fis != null) {fis.close(); }} catch (IOException e) {e.printStackTrace();}}System.out.println("执行了");}
}
异常声明处理:throws(延后处理):一个方法产生了一个它不想立即处理的异常时,那么就需要在该方法的头部使用throws主动声明这个异常,告诉调用者可能出现问题,后面交给外部进行处理,以达到延后处理的目的。如下:m1方法中的异常最终交给了m3方法解决。
/* 关键字:throws 格式:public ... 返回值类型 方法名(形参列表) throws 异常类型1,异常类型2,...,异常类型n{//方法体}*/
public class TestException_4 {public static void main(String[] args) {m3();}public static void m3(){try {m2();} catch (IOException e) {e.printStackTrace();}}public static void m2() throws IOException {m1();}public static void m1() throws IOException {FileInputStream fis = new FileInputStream("hello.txt");int len;while ((len = fis.read()) != -1) {System.out.println((char) len);}fis.close();}
}
throw 关键字用来在程序中明确的抛出异常,什么是抛出异常呢?为了明确指出此方法不捕获这类异常,并且要让调用该方法的其他方法去捕获处理该异常。我们就使用throws明确的抛出(产生)该类异常
/*演示手动抛出异常对象=关键字:throw*/
public class TestException_5 {public static void main(String[] args) {People p = new People("张三", 140);System.out.println(p);//...}
}
//------------------------------分割----------------------------
class People{private String name;private int age;public People() { }public People(String name, int age) {this.name = name;//数据合理性的校验if (age <= 0 || age > 130) {
// System.out.println("年龄不合理..."); //打印信息
// throw new RuntimeException("年龄有误!"); //抛出异常,因为是运行时异常可以处理也可以不处理
// throw new Exception("年龄有误..."); //抛出异常,因为不是运行时异常所以必须处理throw new MyException("年龄有误...");//抛出异常,因为是运行时异常的子类可以处理也可以不处理} else {this.age = age;}}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "People{" +"name='" + name + '\'' +", age=" + age +'}';}
}
潘安:5.final,finally,finalize区别
老王:
①final用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
②finally是异常处理语句结构的一部分,表示总是执行。
③finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的finalize方法, finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。例如关闭文件等。JVM不保证此方法总被调用
潘安:今天就面试到这吧,你没通过。去别的公司面试吧。
老王:为什么?
潘安:我们公司以貌取人。
第四幕 、
第一场)大厦楼下门口
【面试者老王,门卫甲,门卫乙,面试者奥斯卡】
门卫甲:天下熙熙皆为利来,天下攘攘皆为利往,像门卫乙和我这样不为名利专心看门,世界上又有多少人呢?
门卫乙:天下英雄,唯门卫甲与乙耳!
老王:面试失败了,找个保安工作吧,先问问情况。保安大哥你们平时都干嘛呢?
门卫甲:一般讨论Java面试题,总不能一直干这行啊。:1.说一下集合体系
门卫乙:分为单列集合体系和双列集合体系。如图:
单列集合:顶层接口Collection
一、列表(List)集合区分元素的顺序,且允许包含重复元素。
①List:列表,元素是有序的(元素带角标索引),可以有重复元素,可以有null元素。
②ArrayList:底层的数据结构是数组数据结构,特点是查询速度快(因为带角标),但是增删速度稍慢,因为当元素多时,增删一个元素则所有元素的角标都得改变,默认长度是10,当超过长度时,按50%延长集合长度。线程不同步。
③LinkedList:底层数据结构式链表数据结构(即后面一个元素记录前一个)
特点:查询速度慢,因为每个元素只知道前面一个元素,但增删速度快,因为元素再多,增删一个只要让其前后的元素重新相连即可,线程不同步的。
④Vector:底层数据结构是数组数据结构.特点是查询和增删速度都很慢。默认长度是10,当超过长度时,按100%延长集合长度。线程同步。
一般情况下,使用哪种List接口下的实现类呢?
如果要求增删快,考虑使用LinkedList
如果要求查询快,考虑使用ArrayList
如果要求线程安全,考虑使用Vector。
二、集(Set):Set集合中不区分元素的顺序,不允许出现重复元素。
①HashSet:底层数据结构是哈希表、存取速度快、元素唯一、线程不同步。
②TreeSet:底层数据结构式二叉树。可以对Set集合中的元素进行排序。元素有序、线程不同步。
双列映射:顶层接口Map
三、Map集合存储的是键值对,而且键是唯一的,Map和Set很像,Set集合底层就是使用了Map集合。Map集合没有迭代器,要取出元素必须先将Map集合转换成Set集合才能遍历元素
①HashTable: 底层是哈希表数据结构;不可以使用null键和null值;用作键的对象必须实现hashCode和equals方法来保证键的唯一性。线程同步效率低
②HashMap:底层是哈希表数据结构;允许使用null键和null值;线程不同步,效率高;
③TreeMap:底层是二叉树结构;允许使用null键和null值;线程不同步;
老王:都说Java卷,更有卷似Java者。保安这一行也不好干啊。咦奥斯卡你怎么这么快就面试完出来了。
奥斯卡:面试官问我:2.HashMap的工作原理是什么
我没回答出来。
老王:HashMap的工作原理是
①Java 中的 HashMap 是以键值对(key-value)的形式存储元素的。HaspMap的key可以为null。
②HashMap 需要一个 hash 函数,它使用 hashCode()和 equals()方法来向集合添加和检索元素。
③当调用 put()方法的时候,HashMap 会计算 key 的 hash 值,然后把键值对存储在集合中合适的索引上。如果 key已经存在了,value 会被更新成新值。HashMap 的一些重要的特性是它的容量,负载因子和扩容极限。当put的时候大于等于容量的0.75时,会进行扩容。
④多线程环境下若使用HashMap需要使用Collections.synchronizedMap()方法来获取一个线程安全的集合
奥斯卡:“美人赠我锦绣段,何以报之青玉案”,谢谢你告诉我答案。你面试怎么样?肯定也没过吧。
老王:惜败。说说:3.HashMap,TreeMap,HashTable的区别?
奥斯卡:幸好我以前写过这个的博客:集合博客
区别 | 存储 | 底层 | 如何选择 | key是否允许null | 是否线程同步 |
---|---|---|---|---|---|
HashMap | 存储无序 | 哈希表 | 不需要排序 | 允许 | 非线程安全 |
HashTable | 存储无序 | 哈希表 | 需要线程安全 | 不允许 | 线程安全 |
TreeMap | 存储无序 | 红黑树 | 需要排序 | 不允许 | 非线程安全 |
LinkedHashMap | 存储有序 | 链表和哈希表 | 需要存储有序 | 允许 | 非线程安全 |
老王:说说:4.数组(Array) 和列表(ArrayList) 有什么区别
奥斯卡:如下:
数组 | 集合 |
---|---|
固定长度,无法改变 | 长度可改变,可以扩容 |
可以存储基本类型数据,还可以存储引用类型的数据,但是只能存储相同类型的数据。 | 只能存储引用类型的数据并且可以存储不同引用类型的数据。 |
老王:说说:5.list集合与set集合有什么区别
奥斯卡:如下表:可以看出来很复杂。
特点 | List | Set |
---|---|---|
重复元素 | 可以重复 | 不可重复 |
下标 | 有下标 | 没有下标 |
存储 | 有序,可存储多个null | 无序,只能存一个null |
函数 | 拥有独有函数 | 无特有函数,均来自Collection |
遍历 | 有下标可多种方式遍历 | 无下标只能用迭代器遍历 |
数据结构 | ArrayList:底层是可变数组,线程不安全效率高 | HashSet:数据结构是哈希表,无序 |
数据结构 | LinkedList:底层是链表 | TreeSet:底层数据结构是二叉树,有序 |
数据结构 | Vector:底层是可变数组,线程安全效率低 | LinkedHashSet:底层是哈希表和链表,与别的set集合不同,它是有序的 |
老王:休息一下,下一场见。
第二场)大门口
【面试者老王,门卫甲,门卫乙,面试者奥斯卡】
门卫甲:滚滚长江东逝水,浪花淘尽英雄。我去东边卫生间逝个水,门卫乙你先蹲这看会儿门。
门卫乙:去卫生间你记得把你右后腿抬起来滋。
老王:你俩真有趣,不如我们继续讨论面试题吧。
6.Collections和Collection的区别
门卫乙:
①Collection是个java.util下的接口,它是各种集合结构的父接口,定义了集合对象的 基本操作方法。
②Collections是个java.util下的工具类,它包含有各种有关集合操作的静态方法,主要是针对集合类的一个帮助类或者叫包装类,它提供一系列对各种集合的搜索,排序,线程安全化等操作方法。
老王:那么。
7.Comparable和Comparator接口是干什么的?有什么区别。
门卫乙:== ①Comparable 接口只包含compareTo()方法。可给两个对象排序。(返回负数,0,正数来表明输入对象小于,等于,大于已经存在的对象。)
② Comparator 接口包含 compare()和 equals()两个方法。compare()方法用来给两个输入参数排序,返回负数,0,正数表明第一个参数是小于,等于,大于第二个参数。equals()方法需要一个对象作为参数,它用来决定输入参数是否和 comparator 相等。只有当输入参数也是一个 comparator 并且输入参数和当前 comparator 的排序结果是相同的时候,这个方法才返回 true。==
老王:最后一个问题。
8.集合中有哪些线程安全类,哪些是不安全的,哪些是支持排序的类?
奥斯卡:
①线程安全类:Vector、Hashtable、Stack。
②线程不安全的类:ArrayList、Linkedlist、HashSet、TreeSet、HashMap、TreeMap
③支持排序的类有HashSet、LinkedHashSet、TreeSet等
④一个是不支持排序的List接口,一个是有自身排序的Set接口
第五幕 、
第一场)街边
【衣衫褴褛老者,保洁阿姨,面试者老王】
衣衫褴褛老者:“可怜身上诺基亚,心忧Java Offer少”
老王:这位老人家有故事,“老人家,1.Java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
衣衫褴褛老者:字节流,字符流两种类型流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。其它与IO操作相关的类都是派生至上述4个抽象类。如字节相关的:FileInputStream、FileOutputStream类;字符相关的:BufferedReader、BufferedWriter类
衣衫褴褛者:什么老人家,我今年才30,公司裁员,我又买了烂尾房才沦落到这个地步啊。
老王:竟然是前辈,赶紧请教一下:大哥,2.什么是序列化,如何实现序列化?请解释Serializable接口的作用。
衣衫褴褛者:你是把我当npc了嘛,一个问题接着一个问题。
①我们的对象不只是存储在内存中,序列化技术将java对象变成字节流的形式传出去转换成一串由二进制字节组成的数组,然后将这二进制数据保存在磁盘或传输网络。通过反序列化从一个字节流中恢复成一个java对象,达到对象持久化的目的。。
②jre本身就提供了这种支持,被传输的对象必须实现serializable接口该接口是一个标记接口,其中没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,这样javac编译时就会进行特殊处理,这样才可以被OutputStream的writeObject方法操作,这就是序列化。
老王:你懂的这么多,竟然也被裁了。那么3.java里面的io跟nio有什么区别
衣衫褴褛者:比不上年轻人啊
①Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的,IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。
②Java IO的各种流是阻塞的。而Java NIO的是非阻塞模式 。阻塞式IO,每个连接必须要开一个线程来处理,不能使用线程池来处理,并且没处理完线程不能退出,在连接数不多的情况下,传统IO编写容易方便使用连接数较多将会出现资源不足的情况。 非阻塞式IO,可以利用线程池来处理。因为非阻塞IO处理连接是异步的。当某个连接发送请求到服务器,服务器把这个连接请求当作一个请求"事件",并把这个"事件"分配给相应的函数处理。我们可以把这个处理函数放到线程中去执行,执行完就把线程归还。这样一个线程就可以异步的处理多个 事件。
③选择器上,Java IO无选择器,而NIO有选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
老王:真复杂啊,看不懂,先背着吧。
衣衫褴褛者:“没关系呦,少年郎啊,年少不得之物终将困其一生,暮年浮光之景,又终会因一物一事而解终生之惑。将之瞬息点醒。”
2024年3月找到工作了,全剧终。