1:Java 特性
1-1:Java 特点
- 简单性
- 面向对象
- 分布式
- 健壮性
- 安全性
- 体系结构中立
- 可移植性
- 解释型
- 高性能
- 多线程
- 动态性
1-2:JVM\JRE\JDK\JIT
- JDK:
Java Development Kit
的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。 - JRE:
Java Runtime Environment
的简称,Java 运行环境,为 Java 的运行提供了所需环境。 - JVM:
Java Virtual Machine
,Java 虚拟机。 - JIT:
Just-In-Time Compiler
,即时编译器。
JDK 包含 JRE 和编译器 Javac,还包含很多 Java 程序调试和分析的工具。JRE(Java 运行时环境)包含虚拟机但是不包含编译器。
简单来说:如果需要运行 Java 程序,只需安装 JRE ,如果需要编写 Java 程序,则需要安装 JDK。
1-3:变量的初始化顺序
- 父类静态代码块
- 子类静态代码块
- 父类成员代码块
- 父类构造函数
- 子类成员代码块
- 子类构造函数
静态代码块和非静态代码块按顺序加载。
1-4:⾯向过程性能⽐⾯向对象⾼
因为类加载过程需要实例化,比较消耗资源,但是面向对象的特征又使得程序易维护,易复用,易扩展。
但是面向过程也需要分配内存,计算内存偏移量,Java 性能差的主要原因并不是因为它是面向对象语言,而是 Java 是半编译语言,最终的执行代码并不是可以直接被 CPU 执行的二进制机械码。而面向过程语言大多都是直接编译成机械码在电脑上执行,并且其它一些面向过程的脚本语言性能也并不一定比 Java 好。
1-5:持久化对象三种状态
瞬态,持久化,托管。
2:Java 基础-面对对象(OOP)
2-1:面向对象的原则
- 单一职责原则(Single Responsibility Principle)
每一个类应该专注于做一件事情。 - 里氏替换原则(Liskov Substitution Principle)
超类存在的地方,子类是可以替换的。 - 依赖倒置原则(Dependence Inversion Principle)
实现尽量依赖抽象,不依赖具体实现。 - 接口隔离原则(Interface Segregation Principle)
应当为客户端提供尽可能小的单独的接口,而不是提供大的总的接口。 - 迪米特法则(Law Of Demeter)
又叫最少知识原则,一个软件实体应当尽可能少的与其他实体发生相互作用。 - 开闭原则(Open Close Principle)
面向扩展开放,面向修改关闭。 - 组合/聚合复用原则(Composite/Aggregate Reuse Principle CARP)
尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。
2-2:Java 类以及类的成员
2-2-1:属性
2-2-1-1:属性与局部变量的相同点、不同点
- 属性直接声明在类里边,方法外边的变量为属性变量,直接声明在方法里边的变量为局部变量。
- 属性变量有默认值,在使用之前不要求一定赋值,局部变量没有默认值,在使用之前一定要被赋值。
- 属性变量可以有权限修饰符修饰,局部变量不能用权限修饰符修饰。
- 属性变量的作用范围在整个类中,局部变量的作用范围在所属代码段中。
- 属性变量可以由 static 修饰,局部变量不能由 static 修饰。
2-2-2:Java 基础-构造器
2-2-2-1:⼀个类的构造方法的作⽤是什么
完成对象的初始化。
2-2-2-2:构造方法有哪些特性?
无返回值,不可重写,不可以用static
,synchronized
,abstart
,final
修饰。方法名和类名相同。
2-2-2-3:构造方法可不可以被重写和重载
构造方法可以重载,不可以重写。
2-2-2-4:构造函数能用 private 修饰吗
构造方法可以用 private 修饰,保证只有内部方法可以通过这个构造。
2-3:面对对象三大特性
来自维基百科:面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的编程典范,同时也是一种程序开发的抽象方针。它可能包含数据、属性、代码与方法。对象则指的是类(class)的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关联的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。
2-3-1:封装
封装(数据安全):把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
2-3-1-1:封装的应用场景
- 控制访问范围
- 代码复用
- 隐藏实现细节
2-3-2:继承
2-3-2-1:什么是继承
继承(代码复用):在已存在的类的基础上扩展新类,提高代码的复用,程序的可维护性,节省大量创建新类的时间,提高效率。
2-3-2-2:继承的应用场景
对现存场景修改,维护
2-3-3:多态
多态(动态绑定):多态是同一个行为具有多个不同表现形式或形态的能力。
2-3-3-1:多态的必要条件(实现方式、机制)
- 继承(实现)
- 重写
- 父类引用指向子类实例
2-3-3-2:多态的好处
在不同情形可以更方便的调用不同方法,设计模式就经常使用多态来实现。通过实现同一接口的不同具体实现,来调用方法名相同但是具体效果不同的方法。
2-3-3-3:多态的例子
策略模式,状态模式,设计模式基本都有多态。
2-4:抽象
2-4-1:Java 抽象类可以有构造函数吗?作用是什么
抽象类可以有构造函数,虽然抽象类不能实例化,但是子类可以通过父类构造函数初始化父类属性。
2-4-2:Java 抽象类可以实现接口吗? 它们需要实现所有的方法吗
抽象类可以实现接口,不需要实现所有,因为抽象类自身也有抽象方法。
2-4-3:Java 抽象类可以是 final 的吗
不可以,因为抽象类就是用来继承的。
2-4-4:Java 抽象类可以有 static 方法吗
可以
2-4-5:可以创建抽象类的实例吗(new 类)
不可以
2-4-6:抽象类必须有抽象方法吗
不一定,只是无论有没有抽象方法抽象类都不能实例化。
2-4-7:何时选用抽象类而不是接口
接口被用于常用的功能,便于日后维护和添加删除,而抽象类更倾向于充当公共类的角色,不适用于日后重新对立面的代码修改。功能需要累积时用抽象类,不需要累积时用接口。
2-4-8:Java 中的抽象方法是什么
用abstract
修饰的没有方法体的方法。
2-4-9:Java 抽象类中可以包含 main 方法吗
可以
2-5:面对对象区别
2-5-1:接⼝和抽象类的区别
相同点:
- 都不能被实例化。
- 接口的实现类或抽象类的子类都只有实现了接口或抽象类中的抽象方法后才能实例化。
不同点:
- 接口只有定义,不能有方法的实现,Java 1.8 中可以定义 default 方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
- 实现接口的关键字为 implements,继承抽象类的关键字为 extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
- 接口强调特定功能的实现,而抽象类强调所属关系。
- 接口成员变量默认为 public static final,必须赋初值,不能被修改;其所有的成员方法都是 public、abstract 的。抽象类中成员变量默认 default,可在子类中被重新定义,也可被重新赋值;抽象方法被 abstract 修饰,不能被 private、static、synchronized 和 native 等修饰,必须以分号结尾,不带花括号。
- 接口被用于常用的功能,便于日后维护和添加删除,而抽象类更倾向于充当公共类的角色,不适用于日后重新对立面的代码修改。功能需要累积时用抽象类,不需要累积时用接口。
2-5-2:继承和实现区别
一个类只能继承一个类,但是能实现多个接口。
2-5-3:为什么要设计接口、抽象类还有实现类
设计模式中的桥接模式其实就是将一个类的复杂属性用抽象类和接口拆分。例如红球,篮球,红色正方体,蓝色正方体,将球和正方体拆分为抽象类形状,颜色拆分为接口。通过这种方式使代码易维护,易扩展,易复用。
2-6:Java 基础-关键字
2-6-1: 通过反射访问 private 成员和方法,既然能访问为什么要 private?
private 并不是严格的代码安全,只是提供一般情况下的封装,做到不暴露细节。
2-6-2:static 关键字
2-6-2-1:static 使用场景
- 修饰成员变量和成员方法
- 静态代码块
- 修饰类(只能修饰内部类)
- 静态导包(用来导入类中的静态资源,1.5 之后的新特性)
2-6-2-2:静态方法和实例方法有何不同
- 外部调用方法时,可以直接通过类型调用静态方法,而实例方法必须用对象调用。
- 静态方法内部不能调用实例变量和实例方法。而实例方法都可以。
2-6-2-3:静态变量和实例变量的区别?
静态变量属于类,实例变量属于对象。
2-6-2-4:static 方法可以访问非 static 方法或变量吗?
不可以
2-6-3:final 关键字
2-6-3-1:final 关键字使用场景
- final 修饰类,表示类不可继承。
- final 修饰方法,表示方法不可重写。
- final 修饰常量,表示常亮不可更改,必须显式初始化。但是常亮如果式引用类型,只是引用不能改变,对象属性可以改变。
2-6-3-2:final, finally, finalize 的区别
- final 是修饰符,用来修饰类,方法,常量。
- finally 是代码块,用于执行 try-catch 的收尾。
- finalize 是垃圾回收方法,用于进行垃圾回收。
2-6-3-3:使用 final 关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
引用不能变,对象属性可以改变。
2-6-4:this 关键字和 super 关键字
this 关键字调用本对象,super 调用父类。
2-6-5:transient 关键字
transient 关键字用于不参与序列化。
2-7:Java 基础-重载与重写
2-7-1:重载与重写
- 重载发生在一个类中,同一个方法根据传入数据的不同,做出不同的处理。
- 重写发生在父类和子类中,传入数据相同,返回值相同。
区别点 | 重载 | 重写 |
---|---|---|
发生范围 | 同一个类 | 子类 |
参数列表 | 必须修改 | 不能修改 |
返回类型 | 可修改 | 子类更小或相等 |
异常 | 可修改 | 更小或相等 |
访问修饰符 | 可修改 | 子类访问范围更大或相等 |
发生阶段 | 编译器 | 运行期 |
返回类型为 void 或基础类型不可修改,引用类型可以使用子类
override(重写):
- 方法名、参数、返回值相同。
- 子类方法不能缩小父类方法的访问权限。
- 子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
- 存在于父类和子类之间。
- 方法被定义为 final 不能被重写。
overload(重载):
- 参数类型、个数、顺序至少有一个不相同。
- 不能重载只有返回值不同的方法名。
- 存在于父类和子类、同类中。
2-7-2:Java 中是否可以覆盖(override)一个 private 或者是 static 的方法?
不可以
- private 方法只有父类能访问,子类不能访问。所以不能重写。
- static 方法是编译时绑定,重写是运行时绑定,形式上可以重写,但是实际上不起作用。
2-8:Java 基础-值传递与引用传递
2-8-1:值传递与引用传递的概念
- 值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
- 引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
2-8-2:值传递与引用传递的实例举证
基础类型就是值传递,引用类型例如数组,字符串也是值传递,是将引用复制一份传递给方法。
2-9:Java 基础-深拷贝与浅拷贝
2-9-1:深拷贝与浅拷贝
- 如果类中属性有自定义引用类型,浅拷贝只拷贝引用,不拷贝引用指向的对象。浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝。
- 深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝(例:JSON.parse()和 JSON.stringify(),但是此方法无法复制函数类型)。
2-9-2:浅拷贝方法
实现 Cloneable 接口,重写 clone 方法。
2-9-3:深拷贝方法
实现 Cloneable 接口,重写 clone 方法将属性对象也调用 clone 方法。或者实现 Serializable 接口。
2-9-4:赋值和浅拷贝的区别
赋值时直接将引用引向堆内存,浅拷贝的问题是只是复制一个引用。
2-10:equals 与 hashcode
2-10-1:为什么要重写 hashcode 与 equals
因为 hashcode 和 equals 方法是 Object 类的方法,如果不重写 equals(),则会直接比较引用。源码如下:
1 | public boolean equals(Object obj) { |
如上,如果不充写 equals()方法则二者不可能相等。而 hashcode 和 equals 的关系是:
- hashcode 不等,equals()一定不等
- hashcode 相等,equasl()也不一定相等
- equasl()相等,hashcode 一定相等
- equasl()不等,hashcode 也一定不等
这四条定义是为了保证 HashMap 这一类使用 hash 的类计算不出错。虽然重写 equals()方法不重写 hashCode()方法也不会报错。
但是在 HashMap 的比较中,是根据 hashcode 先比较,在使用 equals()进行比较。如果不重写 hashCode()方法有极大概率永远也不会相等。那么 HashMap 就废掉了。
2-10-2:重写 equals 不重写 hashcode 会出现什么问题
HashMap 无法比较,无法 get。
2-10-3:为什么两个对象有相同的 hashcode 值,它们也不一定是相等的?
不重写的时候,hashcode 是根据 jvm 内存地址经过运算而来,所以一般是不相等的。而重写后根据重写后的规则才可能相同。
2-10-4:阿里关于 hashcode 和 equals 的处理规则
关于 hashCode 和 equals 的处理,遵循如下规则:
- 只要覆写 equals,就必须覆写 hashCode。
- 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆写这两种方法。
- 如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals。
2-10-5:hashcode 和 equals 源码写一下
1 | public boolean equals(Object obj) { |
hashcode 是个 native 方法
2-11:i++与++i 的问题
2-11-1:i++和++i 的区别,及其线程安全问题
i++是调用 i 的值,再+1,++i 是先+1,让调用 i 的值。这两个都是线程不安全的。
i++
其实是取值,运算,赋值三步操作
1 | int temp = i |
如果 a,b 两个线程同时进行 i++,a 先读取 i 的值为 100,随后切换线程 b 读取 i 的值也为 100。然后 a 进行 100+1 并赋值,b 也进行 100+1 并赋值。最后的结果为 i=101,实际运算结果为 i=102。
2-11-2:i++和++i 是否为原子操作
不是
2-11-3:如何实现 i++和++i 的原子性呢?
atomic 包
2-12:数据类型
2-12-1:八种数据类型是什么?
byte,short,int,long,float,double,char,boolean
2-12-1-1:Java 为什么除了基本数据类型还要有引用数据类型
Java 是面向对象语言,准确来说是只有引用类型的,而基础数据类型是为了减少资源消耗,加快运行速度推出的。
2-12-1-2:String 为什么不是基本数据类型
String 内部其实是个 char[]数组,所以是引用类型
2-12-1-2:引用类型有哪几种
除了基础类型都是引用类型
2-12-2:数据类型的范围
- byte:一个字节,
-2^7-1~2^7
,-128~127 - short:两个字节,
-2^15-1~2^15
,-32768~32767 - int:四个字节,
-2^31-1~2^31
,-2147483648~2147483647 - long:八个字节,
-2^63-1~2^63
,-9223372036854774808~9223372036854774807 - float:四个字节,3.402823e+38 ~ 1.401298e-45
- double:八个字节,1.797693e+308~ 4.9000000e-324
- char:两个字节
- boolean:四个字节
2-12-2-1:为什么 byte 类型是-128~+127
- 计算机中负数是用补码的形式保存、并用它参与加减法运算的,减法会被转换为加法,计算机中没有减法运算。
- 反码是为了解决减法运算,补码是为了解决反码产生的 ±0 的问题。参考(https://blog.csdn.net/boatalways/article/details/17027573)
- 对人而言二进制所代表的值一定是从原码求出的,开头如果是 1 的二进制,一定要说明其是原码、反码还是补码。
- 在原码、反码、补码相互转换以及求对应的十进制求值时,符号位是绝不参与的,但是在加减过程中,是参与位运算的。
- 计算机中规定了+0 对应的二进制就是 0,那么-0 就没有意义了,必须找一个数和它对应。
- byte 的最小值-128、short 的最小值-32768、int 的最小值-2147483648 都是用对应的-0 的原码来进行表示,这是人为规定的、人为规定的、人为规定的。但是这么规定又很巧妙,妙在上述 10 中的三点。
2-12-3:自动拆装箱
2-12-3-1:为什么要有自动拆装箱
基础类型是为了使运算更快,而包装类型是为了使基础类型具有类的特性。有了这两样东西有时候就需要进行转换,所以就有了自动拆装箱。
2-12-3-2:自动拆装箱的原理
自动装箱都是通过包装类的 valueOf()
方法来实现的.自动拆箱都是通过包装类对象的 xxxValue()
来实现的。
2-12-3-3:自动拆装箱使用场景
- 将基础类型放入集合 - 自动装箱
- 包装类型和基本类型的大小比较 - 自动拆箱
- 包装类型的运算 - 自动拆箱
- 三目运算符的使用 - 自动拆箱
- 函数参数与返回值
2-12-3-4:自动拆装箱带来的问题
- 因为包装类型是对象,缓存范围(-128-127)之外的比较需要使用 equals
- 有些场景会自动拆装箱,如果包装类型为 null,自动拆箱可能会抛出 NPE 错误
- 循环中大量拆装箱,浪费资源
2-12-4:Integer 缓存机制
2-12-4-1:int 与 Integer 区别
- 一个是基础类型,一个是包装类型
- Integer 变量必须实例化后才能使用;int 变量不需要;
- Integer 实际是对象的引用,指向此 new 的 Integer 对象;int 是直接存储数据值
- Integer 的默认值是 null;int 的默认值是 0。
- int 可以用
==
比较,而 Integer 在缓存池范围内可以用==
,范围外必须用 equals()
2-12-5:String 转出 int 型, 判断能不能转? 如何转?
Integer.parseInt();
Integer.valueOf();
2-12-6:short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1+=1;有什么错?
因为s1+1
会自动进行类型转换,变为 int 型,而+=
是 Java 规定的运算符,所以不会报错。
2-13:对象比较有几种方式?
- 重写 Object 类的 equals()方法;
- 实现 Comparable 接口,并实现 compareTo()方法;
- 定义一个单独的对象比较器,继承自 Comparator 接口,实现 compare()方法。
2-13-1:Comparator 与 Comparable 有什么区别?
- Comparable 是排序接口;若一个类实现了 Comparable 接口,就意味着“该类支持排序”。
- 而 Comparator 是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
- Comparable 相当于“内部比较器”,而 Comparator 相当于“外部比较器”。
2-14:数组的特点?底层如何定位到数组中的元素?数组的内存空间是连续的吗?
数组查询快,增删慢。数组底层是一块连续的内存空间,访问地址时根据内存块的首地址根据公式确定访问下标对应地址。
- Java 中数组的引用放于栈内存中,而实际数据放于堆内存中。
- 如果数组是引用类型数组,则堆内存中对应地址则指向对应对象。
- 多维数组其实也是一维数组,每一格都指向下一层数组。例如二维数组存放行的一维数组,每一格指向列的一维数组。