特性
面向对象和面向过程有什么区别
-
面向对象:以对象为中心,叭要解决的问题分解成各个对象。每一个都可以看做一个对象,每个对象有自己的属性和行为,对象与对象之间通过方法来交互。
比如五子棋分为三个对象,黑白双方,棋盘系统,规则系统
第一类对象(黑白双方)负责接受用户输入,并告知第二类对象(棋盘系统)棋子布局的变化,棋盘系统接收到了棋子的变化,并负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。
-
面向过程:以事件为中心的编程思想,把问题的解决步骤分析出来,然后用函数把这些步骤实现,在一步步的具体步骤中调用函数。
比如下五子棋,步骤就是开始游戏,黑子先走,绘制画面,判断输赢,轮到白子,绘制画面,判断输赢,输出结果。
面向过程关注的就是怎么一步步判断棋局输赢,通过控制代码,从而实现函数的顺序执行。
Java面向对象性能差的原因:是半编译语言,最终的执行代码并不是可以被CPU执行的机器码。
Java语言有什么特点,与C++有什么不同?
-
平台无关性(编译一次,运行多次)
Java虚拟机执行编译好的字节码文件,实现跨平台
-
Java不提供指针来直接访问内存,程序内存更加安全
-
Java有自动内存管理机制,不需要程序员手动释放无用内存
Java虚拟机是什么
Java虚拟机是运行字节码文件的虚拟机,Java虚拟机针对不同系统有特定的实现,目的是运行相同的字节码,得到相同的结果。Java编译一次运行多次的关键就是虚拟机和字节码文件。
Java三大特性
-
封装
把对象的属性私有化,同时提供一些可以被外界访问的属性和方法,Java里面提供了不同的权限修饰符来控制外界的访问权限。
-
继承
使用已经存在的类作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但是不能选择性的继承父类。但是父类的私有方法或者属性无法访问,只是拥有。
-
多态
就是指程序中一个引用变量到底会指向那个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须由程序运行期间才能决定。也就是说事务在运行过程中存在不同的状态。
多态的存在有三个前提:
- 要有继承关系
- 子类要重写父类的方法
- 父类引用指向子类对
**多态的弊端:**不能使用子类特有的成员属性和子类特有的成员方法。
多态的实现方式
-
基于继承实现的多态
基于继承的实现机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。
-
基于接口实现的多态
继承是通过重写父类的同一方法的几个子类来体现,那么就可通过实现接口并覆盖接口中同一方法的不同类体现。
数据类型
基本类型
- byte 1字节
- short 2字节
- char 2字节
- int 4字节
- float 4字节
- double 8字节
- long 8字节
- boolean JVM在编译时会将boolean类型转换为int,将boolean数组通过读写byte数组来实现
==和equal的区别
==判断两个对象的地址是不是相等。对于基本类型,比较的是值,引用类型比较的内存地址
equal一般有两种使用情况:
- 如果没有重写equal方法,那么和==作用相等
- 如果重写了,一般都是覆盖equal方法来比较两个对象的内容是否相等。
Object类有哪些方法
-
hashCode()
返回散列值,相等的两个对象散列值一定相等,但是散列值相等的两个对象不一定等价。
所以在覆盖equals()方法时,应当总是覆盖hashCode()方法,保证等价的对象散列值也相等。
-
equals(Object obj)
-
clone()
clone() 是 Object 的 protected 方法,它不是 public,要使用这个方法需要重写 clone(),并且实现cloneable接口。
-
toString()
默认返回的是散列码的十六进制表示。
-
getClass()
返回Class对象
-
finalize()
-
notify()
-
notifyAll()
-
wait()
-
wait(long timeout)
-
wait(long timeout,int nanos)
装箱和拆箱
装箱:将基本类型转换为引用类型
拆箱:将引用类型转换为基本类型
缓存池
new Integer(123) 与 Integer.valueOf(123) 的区别:
new Integer(123)每次都会创建一个对象
Integer.valueOf(123)会使用缓存池中的对象,多次调用会取得同一个对象的引用
字符串常量池
在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。
new String(“abc”)
使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 “abc” 字符串对象)。
- “abc” 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 “abc” 字符串字面量;
- 而使用 new 的方式会在堆中创建一个字符串对象。
String,StringBuilder,StringBuffer的区别
-
String内部用char数组存储数据,被声明为final,意味着数组初始化之后不能再引用其他数组,并且String内部没有改变value数组的方法,因此可以保证String不可变
-
StringBuilder,StringBuffer都继承自abstractStringBuilder类,用char数组存储数据,没有用final声明,是可变的。
-
区别:
-
可变性
String是不可变的,StringBuilder,StringBuffer是可变的
-
线程安全
String不可变,因此线程安全。
StringBuilder是线程不安全的。
StringBuffer是线程安全的,内部使用synchronized同步
-
性能
每次对String类进行改变,都会生成一个新的String对象,然后将指针指向新的String对象。
但是StringBuilder,StringBuffer都是对对象本身进行操作,而不是生成新的对象并改变对象引用。
-
值传递
Java将一个参数传入一个方法时,本质是将对象的地址以值的方式传递到形参中。因此在方法中使指针引用其他对象,那么这两个指针此时指向的是完全不同的对象,在一方改变其所指向对象内容时另一方没有影响。
如果在方法里面改变对象的字段值会改变原对象该字段值,因为改变的是同一个地址指向的内容。
深拷贝和浅拷贝
浅拷贝:对基本类型值传递,拷贝对象和原对象的引用类型引用同一对象。对引用类型,如果改变新建对象的内容,原来对象的内容也会改变。
深拷贝:对基本类型值传递,对引用类型,创建一个新的对象,并复制其内容。
关键字
权限修饰
- protected:该成员只能在其自己的包中访问,此外还可以由另一个包中的该类的子类访问
- public:类或者成员或者方法对所有都可见
- private:该成员只能在其自己的类中访问
final
- 修饰变量
- 基本类型:final使数值不变,为常量
- 引用类型:引用不变,不能引用其他对象,但是被引用的对象本身可以修改
- 修饰类:类不能被继承
- 修饰方法:方法不能被子类重写。
static
-
静态变量
类变量,类所有实例共享静态变量,可以通过类名来访问,在内存中只存在一份
-
静态方法
只能访问所属类的静态字段和静态方法,方法中不能有this和super关键字
-
静态语句块
在类初始化时运行一次
-
静态内部类
不能访问外部类的非静态变量和方法
重写和重载
- 重写:主要是存在于继承体系中,子类实现了一个与父类在方法声明上完全相同的一个方法。重写有以下限制:子类的访问权限必须大于等于父类的;子类的返回值类型必须是父类返回类型或者其子类型;子类方法抛出的异常类型必须是父类抛出异常类型或者其子类型。
- 重载:存在同一个类中,方法名相同,参数类型、个数、顺序至少有一个不同。
抽象类和接口
-
抽象类
- 用abstract关键字声明,如果一个类包含抽象方法,那么这个类必须申明为抽象类。
- 抽象类不能被实例化,需要继承抽象类才能实例化其子类
-
接口
- 接口类默认用public abstract修饰,在JAVA8以前,不能有任何的方法来实现
- 从JAVA8开始,接口可以有默认的方法实现,因为如果接口想要添加一个新的方法,那么要修改所有实现了该接口的类。
- 接口的成员和方法默认是public的,不允许定义为private或者protected。
- 接口的字段默认都是final和static的
-
区别:
- 从使用上来说:接口需要让不相关的类都实现一个方法,例如不相关的类都可以实现Comparable接口中的compareTo()方法。抽象类是在几个相关类中共享代码。
- 一个类可以实现多个接口,但是不能继承多个抽象类
- 接口的字段只能是final和static类型的,但是抽象类没有这种限制
- 接口成员只能是public的,而抽象类的成员可以有多种访问权限
集合容器
容器种类和底层实现
ArrayList
HashMap
反射
概念
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
具体的实现就是因为每个类都有一个Class对象,包含了类信息。获得这个Class对象可以构造任意一个对象以及调用方法。
Java的java.lang.reflect对反射提供了支持,主要包含三个类:
- Filed:可以使用get()和set()方法读取和修改对象关联的字段
- Method:通过Invoke()方法调用与Method对象关联的方法
- Constructor:创建新的对象
用途
最重要的用途就是开发各种通用框架。比如Spring都是配置化的,通过XML文件配置bean。为了保证框架的通用性,可能需要根据不同的配置文件加载不同的对象,这时候就必须用到反射,运行时动态的加载需要加载的对象。
缺点
- 性能开销:涉及了动态类型的解析,所以JVM无法对这些代码进行优化,所以反射操作的效率要比非反射操作低。
- 内部暴露:反射允许执行一些正常情况下不被允许的操作,比如访问私有属性和方法,也会导致代码功能失调。
异常
分类
Throwable类下面主要有两个类,Error和Exception
Error:是程序无法处理的错误,是Java虚拟机生成并抛出的,比如内存溢出,栈溢出。
Exception:
- 有
RuntimeException
(运行时异常),该类型的异常自动为你所编写的程序定义ArrayIndexOutOfBoundsException
(数组下标越界)、NullPointerException
(空指针异常)、ArithmeticException
(算术异常)、MissingResourceException
(丢失资源)、ClassNotFoundException
(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生; - 而
RuntimeException
之外的异常我们统称为非运行时异常,类型上属于Exception
类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException
、SQLException
等
异常关键字
- try…catch…finally、return的执行顺序
- 不管有无异常,finally语句都要执行
- 当try和catch中有return时,finally仍然会执行;
- finally是在return语句执行之后,返回之前执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,不管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前就已经确定了;
- finally中如果包含return,那么程序将在这里返回,而不是try或catch中的return返回,返回值就不是try或catch中保存的返回值了
- throws和throw的区别
- throws出现在方法函数头;而throw出现在函数体。
- throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常。
- 两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
泛型
-
Java中泛型是如何工作的?什么是类型擦除?
泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List在运行时仅用一个List来表示。
-
泛型中的限定通配符和非限定通配符
- 有两种限定通配符,一种是<? extends T>它通过确保类型必须是T的子类来设定类型的上界,另一种是<? super T>它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。
- 另一方面<?>表示了非限定通配符,因为<?>可以用任意类型来替代。
JDK8新特性
Stream流
Stream使用一种类似于SQL语句从数据库查询数据的直观方式,对数据进行如筛选、排序以及聚合等多种操作。
- 数据源:是Stream的来源,可以是集合、数组、I/O channel等转换而成的Stream;
- 基本操作:类似于SQL语句一样的操作,比如filter,map,reduce,find,match,sort等操作。
lambda表达式
函数式接口:只包含一个方法的抽象接口
lambda表达式是一段可以传递的代码,因为他可以执行一次或者多次。和函数式接口配合使用。表达式左边是方法的参数,右边是接口的实现。
接口的默认方法和静态方法
Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。