目录
5、阐述 final、finally、finalize 的区别
6、String 和StringBuilder、StringBuffer 的区别
7、重载(Overload)和重写(Override)的区别
1、什么是面向对象?谈谈你对面向对象的理解
从我们开始接触Java这门语言后,就有人告诉我们这是一个面向对象的语言。说的最多的是new个对象,其实并不知道什么是对象。以为对象就是一个class(类),并不觉得有什么特殊。直到很久之后,面试官问到什么是OOP,嘴巴都是一个大O,OOP?WTF?那人厌恶的说到就是面向对象编程。我说:Java就是面向对象,就是一切以对象为载体,去编程,去面对。面试官: go out ! now!
滚回来的我赶紧看看什么是OOP,Object Oriented Programming,原来就是面向对象的编程啊,还有OOD(面向对象的设计),OOA(面向对象的分析)。那什么是面向对象呢?要想了解这个问题我们要先了解面向过程,这样对比我们就好理解了。
很早很早以前的编程是面向过程的,比如实现一个算术运算1+1 = 2,通过这个简单的算法就可以解决问题。但是随着时代的进步,人们不满足现有的算法了,因为问题越来越复杂,不是1+1那么单纯了,比如一个班级的学生的数据分析,这样就有了对象这个概念,一切事物皆对象。将现实的事物抽象出来,注意抽象这个词是重点啊,把现实生活的事物以及关系,抽象成类,通过继承,实现,组合的方式把万事万物都给容纳了。实现了对现实世界的抽象和数学建模。这是一次飞跃性的进步。
举个最简单点的例子来区分 面向过程和面向对象
有一天你想吃鱼香肉丝了,怎么办呢?你有两个选择
1、自己买材料,肉,鱼香肉丝调料,蒜苔,胡萝卜等等然后切菜切肉,开炒,盛到盘子里。
2、去饭店,张开嘴:老板!来一份鱼香肉丝!
看出来区别了吗?这就是1是面向过程,2是面向对象。
面向对象有什么优势呢?首先你不需要知道鱼香肉丝是怎么做的,降低了耦合性。如果你突然不想吃鱼香肉丝了,想吃洛阳白菜,对于1你可能不太容易了,还需要重新买菜,买调料什么的。对于2,太容易了,大喊:老板!那个鱼香肉丝换成洛阳白菜吧,提高了可维护性。总的来说就是降低耦合,提高维护性!
面向过程是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步一步的实现。
面向对象是模型化的,你只需抽象出一个类,这是一个封闭的盒子,在这里你拥有数据也拥有解决问题的方法。需要什么功能直接使用就可以了,不必去一步一步的实现,至于这个功能是如何实现的,管我们什么事?我们会用就可以了。
面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们我们使用的就是面向对象了。
面向过程:
优点:性能比面向对象好,因为类调用时需要实例化,开销比较大,比较消耗资源。 缺点:不易维护、不易复用、不易扩展.
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护 . 缺点:性能比面向过程差
面向对象的三大特性:1、封装 隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。 2、继承 提高代码复用性;继承是多态的前提。 3、多态 父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。
2、JDK、JRE、JVM区别和联系
一般程序员好多时间都在研究一些并发、多线程、分布式、框架等这些东西,但是突然被人问到jdk、jre、jvm的区别与联系,应该也会瞬间觉得一脸懵逼,感觉还是有必要重新整理一下一些比较基本的java知识了。
名称 | JDK | JRE | JVM |
---|---|---|---|
定义 | Java Develpment Kit java 开发工具 | Java Runtime Environment java运行时环境 | java Virtual Machine java 虚拟机 |
使用人群 | 给开发人员使用 | 普通用户使用 | 普通用户使用 |
详细介绍 | 是java开发工具包,是Sun公司针对java开发人员的产品,jdk中包含jre,在jdk的安装目录下有一个jre的目录,里面有两个文件夹bin和lib,在这里可以认为bin里面就是jvm,lib中是java工作需要的类库,而java和lib合起来就称为jre。 JDK(Java Development Kit) 是整个JAVA的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具(javac/java/jdb等)和Java基础的类库(即Java API 包括rt.jar)。 基本上每个学java的人都会先在机器 上装一个JDK,那他都包含哪几部分呢?在目录下面有 六个文件夹、一个src类库源码压缩包、和其他几个声明文件。其中,真正在运行java时起作用的 是以下四个文件夹:bin、include、lib、 jre。有这样一个关系,JDK包含JRE,而JRE包 含JVM。 | 是运行基于java语言编写的程序所不可缺少的运行环境,也是通过它,java的开发者才可以将自己开发的程序发布到用户手中,让用户使用,与大家熟悉的jdk不同,jre是运行环境,并不是一个开发环境,所以没有包含任何开发工具(如编译器,调试器)等,知识针对使用java程序的用户 JRE(Java Runtime Environment,Java运行环境),包含JVM标准实现及Java核心类库。JRE是Java运行环境,并不是一个开发环境,所以没有包含任何开发工具(如编译器和调试器) JRE是指java运行环境。光有JVM还不能成class的 执行,因为在解释class的时候JVM需要调用解释所需要的类库lib。 (jre里有运行.class的java.exe) JRE ( Java Runtime Environment ),是运行 Java 程序必不可少的(除非用其他一些编译环境编译成.exe可执行文件……),JRE的 地位就象一台PC机一样,我们写好的Win64应用程序需要操作系统帮 我们运行,同样的,我们编写的Java程序也必须要JRE才能运行。 | 就是我们常说的java虚拟机,它是整个java实现跨平台的最核心的部分,所有的java程序首先被编译为.class文件,这种类文件可以在虚拟机上运行,也就是说class并不直接与机器的操作系统相对应。 而是经过虚拟机间接的与操作系统交互,由虚拟机将程序解释给本地系统执行,只有jvm还不能将class执行,因为在解释class的时候jvm需要调用解释所需要的类库lib,而jre包含lib类库,jvm屏蔽了具体操作系统平台的相关信息,使得java程序只需要生成在java虚拟机上运行的目标代码。可以在多种平台(操作系统)上不加修改的运行 |
3、==和equals比较
一、理解”==“的含义
在java中,主要有两个作用。
1、基础数据类型:比较的是他们的值是否相等,比如两个int类型的变量,比较的是变量的值是否一样。
2、引用数据类型:比较的是引用的地址是否相同,比如说新建了两个User对象,比较的是两个User的地址是否一样。OK。到这就注意了,你会发现,我在举引用的例子的时候,使用的是User对象,而不是String。别着急接着往下看。
二、理解equals的含义
先看看他的源码,equals方法是在Object中就有。注意了这里的源码是Object里面的equals。
/*
* @param obj the reference object with which to compare.
* @return {@code true} if this object is the same as the obj
* argument; {@code false} otherwise.
* @see #hashCode()
* @see java.util.HashMap
*/
public boolean equals(Object obj) {
return (this == obj);
}
从这个源码中你会发现,比较的是当前对象的引用和obj的引用是否相同,也就是说比较的默认就是地址。还记的在上面我们使用的是User而不是String嘛?在这里==比较的是引用的地址,equals也是比较的是引用的地址,所以他们的效果在这里是一样的。
public class StringDemo {
public static void main(String args[]) {
int a=1;
int b=1;
System.out.println(a == b); // true
User user1 = new User();
User user2 = new User();
//此时使用的是普通对象User
//所以==和equals的作用是一样的。
System.out.println(user1 == user2); // flase
System.out.println(user1.equals(user2)); // flase
}
}
现在你会发现好像equals的作用和==没什么区别呀,那String类型那些乱七八糟的东西是什么呢?继续往下看马上揭晓。
三、重写equals
1、String中equals方法
看到这个标题相信你已经能找到答案里,Object对象里面的==和equals没有什么区别,这样一看equals方法存在的意义真的不大,不过后来String在Object的基础之上重写了equals,于是功能被大大的改变了。如何重写的呢?我们去String的源码中找寻答案:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
从上面的源码,我们能够获取到的信息是:String中的equals方法其实比较的是字符串的内容是否一样。也就是说如果像String、Date这些重写equals的类,你可要小心了。使用的时候会和Object的不一样。
2、测试String
看看下面的代码:
public class StringDemo {
public static void main(String args[]) {
String str1 = "Hello";
String str2 = new String("Hello");
String str3 = str2; // 引用传递
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // false
System.out.println(str2 == str3); // true
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // true
System.out.println(str2.equals(str3)); // true
}
}
在上面的代码中,定义了三个字符串,分别使用==和equals去比较。为什么会出现这样一个结果呢?还需要从内存的角度来解释一下。
3、内存解释
在java中我们一般把对象存放在堆区,把对象的引用放在栈区。因此在上面三个字符串的内存状态应该是下面这样的。
现在明白了吧。
(1)String str1 = "Hello"会在堆区存放一个字符串对象
(2)String str2 = new String("Hello")会在堆区再次存放一个字符串对象
(3)String str3 = str2这时候Str3和Str2是两个不同的引用,但是指向同一个对象。
根据这张图再来看上面的比较:
(1)str1 == str2嘛?意思是地址指向的是同一块地方吗?很明显不一样。
(2)str1 == str3嘛?意思是地址指向的是同一块地方吗?很明显不一样。
(3)str2 == str3嘛?意思是地址指向的是同一块地方吗?很明显内容一样,所以为true。
(4)str1.equals(str2)嘛?意思是地址指向的内容一样嘛?一样。
(4)str1.equals(str3)嘛?意思是地址指向的内容一样嘛?一样。
(4)str2.equals(str3)嘛?意思是地址指向的内容一样嘛?一样。
OK。现在不知道你能理解嘛?
4、总结:
(1)、基础类型比较
使用==比较值是否相等。
(2)、引用类型比较
①重写了equals方法,比如String。
第一种情况:使用==比较的是String的引用是否指向了同一块内存
第二种情况:使用equals比较的是String的引用的对象内用是否相等。
②没有重写equals方法,比如User等自定义类
==和equals比较的都是引用是否指向了同一块内存。
5、一个小问题
当然了,String类型到这还没结束,有一个小问题需要大家注意一下。比如看下面的代码:
public class ObjectDemo{
public static void main(String[] args) {
String s1 = "Hello";
String s2 = new String("Hello");
s2 = s2.intern();
System.out.println(s1 == s2); // true
System.out.println(s1.equals(s2)); // true
}
}
在这里多了一个intern方法。他的意思是检查字符串池里是否存在,如果存在了那就直接返回为true。因此在这里首先s1会在字符串池里面有一个,然后 s2.intern()一看池子里有了,就不再新建了,直接把s2指向它。
4、final
inal修饰类,表示类不可变,不可继承
比如,String,不可变性
final修饰方法,表示该方法不可重写
比如模板方法,可以固定我们的算法
final修饰变量,这个变量就是常量
注意:
修饰的是基本数据类型,这个值本身不能修改
修饰的是引用类型,引用的指向不能修改
比如下面的代码是可以的
final Student student = new Student(1,"Andy");
student.setAge(18);//注意,这个是可以的!
5、阐述 final、finally、finalize 的区别
final:修饰符(关键字)有三种用法:如果一个类被声明为 final,意味着它不能再派生出新的子类,即不能被继承,因此它和 abstract 是反义词。将变量声明为 final,可以保证它们在使用中不被改变,被声明为 final 的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为 final 的方法也同样只能使用,不能在子类中被重写。
finally:通常放在 try…catch…的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要 JVM 不关闭都能执行,可以将释放外部资源的代码写在 finally 块中。
finalize:Object 类中定义的方法,Java 中允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写 finalize()方法可以整理系统资源或者执行其他清理工作。
6、String 和StringBuilder、StringBuffer 的区别
String 跟其他两个类的区别是
String是final类型,每次声明的都是不可变的对象, 所以每次操作都会产生新的String对象,然后将指针指向新的String对象。
StringBuffer,StringBuilder都是在原有对象上进行操作所以,如果需要经常改变字符串内容,则建议采用这两者。
StringBuffer vs StringBuilder
前者是线程安全的,后者是线程不安全的。 线程不安全性能更高,所以在开发中,优先采用StringBuilder. StringBuilder > StringBuffer > String
7、重载(Overload)和重写(Override)的区别
一般出现在(笔试题-选择题),下面我们说下重点
重载:发生在一个类里面,方法名相同,参数列表不同(混淆点:跟返回类型没关系)
以下不构成重载
public double add(int a,int b)
public int add(int a,int b)
//编译报错
重写:发生在父类子类之间的,方法名相同,参数列表相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法。
面试题:华为的面试题中曾经问过这样一个问题 - “ 为什么不能根据返回类型来区分重载”, 快说出你的答案吧!
8、接口和抽象类的区别
这个问题,要分JDK版本来区分回答:
JDK1.8之前: 语法: 抽象类:方法可以有抽象的,也可以有非抽象, 有构造器 接口:方法都是抽象,属性都是常量,默认有public static final修饰 设计: 抽象类:同一类事物的抽取,比如针对Dao层操作的封装,如,BaseDao,BaseServiceImpl 接口:通常更像是一种标准的制定,定制系统之间对接的标准 例子: 1,单体项目,分层开发,interface作为各层之间的纽带,在controller中注入IUserService,在Service注入IUserDao 2,分布式项目,面向服务的开发,抽取服务service,这个时候,就会产生服务的提供者和服务的消费者两个角色这两个角色之间的纽带,依然是接口 JDK1.8之后: 接口里面可以有实现的方法,注意要在方法的声明上加上default或者static 最后区分几个概念:
多继承,多重继承,多实现 多重继承:A->B->C(爷孙三代的关系) 多实现:Person implements IRunable,IEatable(符合多项国际化标准) 多继承:接口可以多继承,类只支持单继承
抽象类和接口都不能够实例化, 但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现, 否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象, 因为抽象类中可以定义构造器, 可以有抽象方法和具体方法, 而接口中不能定义构造器而且其中的方法全部都是抽象方法。抽象类中的成员可以是 private、默认、protected、public 的,而接口中的成员全都是 public 的。抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类, 而抽象类未必要有抽象方法。
设计原则不同 (中高级程序员)
接口的设计目的,是对类的行为进行约束(更准确的说是一种约束,因为接口不能规定类不可以有什么行为),也就是提供一种机制,可以强制要求不同的类具有相同的行为。它只约束了行为的有无,但不对如何实现进行限制。
抽象类的设计目的,是代码复用。当不同的类具有某些相同的行为(行为集合A),且其中一部分行为的实现方式一致时(A的非真子集,记为B),可以让这些类都派生出一个抽象类。在这个抽象类中实现B,避免让所有的子类来实现B,这就达到了代码复用的目的。而A减B的部分,留给各个子类去实现。正是因为A-B在这里没有实现,所以抽象类不能被实例化出来。
抽象类是对类本质的抽象,表达了is a的关系,比如BMW is a Car,抽象类包含并实现子类的通用特性,将子类存在差异化的特性进行抽象,交由子类去实现。
而接口是对行为的抽象,表达了like a的关系。比如:Bird like Aircraft(像飞行器一样可以飞),但本质上is a Brid。接口的核心是定义行为,即实现类可以做什么,至于实现类的主体是谁,是如何实现的,接口并不关心。
使用场景:当你关注一个事物本质的时候,用抽象类;当拟关注一个操作的时候,用接口。
抽象类的功能要远超过接口,但是,定义抽象类的代价高,因为高级语言来说(从实际设计来说也是),每个类只能继承一个类。在这个类中,你必须继承或者编写出其子类的所有共性。虽然接口在功能上弱化很多,但是它只是针对一个动作的描述。而且你可以在一个中同时实现多个接口。在设计阶段会降低难度。
9、hashCode与equals
hashCode()的作用是获取哈希码,也称为散列码,它实际上是返回一个int整数。这个哈希码的作用是确定对象在哈希表中的索引的位置。hashCode()定义在JDK的Object.java中,java中的任何类都包含有hashCode()函数。散列表存储的是键值对(key-value),他的特点就是:能根据“键”快速的检索出对应的“值”。这其中利用到了散列码!(可以快速找到需要的对象)
为什么要有hashCode
已“hashset如何检查重复“为例来说明为什么要有hashCode:
对象加入hashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,看该位置是否有值,如果没有,hashset会假设对象没有重复出现。但是如果发现有值,这时会调用equals()方法检查俩个对象是否相同。如果二者相同,hashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他的位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。
如果两个对象相等,则hashCode一定也相同。
两个对象相等,对两个对象分别调用equals方法返回true
两个对象有相同的hashCode值,他们也不一定相等。因此,equals方法被覆盖过,则hashCode方法也必须被覆盖。
hashCode()默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这二个对象指向相同的数据)。
10、java中的异常体系
java中所有异常都来自于父类Throwable
Throwable下有两个子类Exception和Error
Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行 。
Exception不会导致程序停止,又分为两个部分RunTimeException运行时异常和CheckedException检查异常。
RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。CheckedException常常发生在编译过程中,会导致程序编译不通过。
ArithmeticException(算术异常)
ClassCastException (类转换异常)
IllegalArgumentException (非法参数异常)
IndexOutOfBoundsException (下表越界异常)
NullPointerException (空指针异常)
SecurityException (安全异常)