面向对象三大特征
封装
将一个对象的属性隐藏在对象内部,不允许外部对象直接访问该对象的内部信息,必须要通过一些方法来操作属性。
继承
使用已经存在的类的定义作为基础建立新的类,新的类可以增加新的数据和功能,也可以用父类的功能。
注意:
- 子类拥有父类所有的属性和方法,但是不能访问父类私有的属性和方法
- 子类可以对父类进行扩展
- 子类可以用自己的方式实现父类的方法
多态
一个对象拥有多种的状态,具体表现父类的引用指向子类对象。
特点:
- 子类重写了父类的方法,会覆盖父类的方法,执行子类的方法
- 对象类型和引用类型之间具有继承或实现的关系
a++与++a
-
当
b = ++a
时,先自增(自己增加 1),再赋值(赋值给 b); -
当
b = a++
时,先赋值(赋值给 b),再自增(自己增加 1)。“符号在前就先加/减,符号在后就后加/减”
基本类型和包装类型区别
- 包装类型可用于泛型,而基本类型不能
- 基本类型的局部变量存放在JVM的虚拟机栈中的局部变量表中,成员变量(未被static修饰)存放在堆中,包装类型的所有变量存放在堆中
- 基本数据类型所占空间比包装类型的小
==
,基本数据类型比较的是值,包装类型比较的是对象的地址,比较值用equals()
方法
重载与重写
- 重载就是同一个类中多个同名方法根据不同的参数来执行不同的逻辑,发生在编译期
- 重写就是子类对父类方法的重新改造,外部样子不改变,内部逻辑可以改变,发生在运行期
- 重写遵循两同两小一大
- 方法名、形参列表相同
- 子类方法的返回类型、抛出的异常比父类方法更小或相等
- 子类方法的访问权限比父类方法更大或相等
接口与抽象类的异同
共同点
- 都不能被实例化
- 都可以包含抽象方法
- 都可以有默认实现的方法
不同点
- 一个类只能继承一个类,但是可以实现多个接口
- 接口中的成员变量只能是
public static final
类型的,不能被修改且无初始值,抽象类的成员变量默认default,可以在子类中被重新定义,也可以被重新赋值
深拷贝、浅拷贝和引用拷贝
- 深拷贝会复制整个对象,包括这个对象所包含的内部对象
- 浅拷贝会在堆上创建一个新的对象,若原对象包含内部对象 ,会复制内部对象的引用地址
- 引用拷贝就是两个不同的引用指向同一个对象
==
与equals()
- 若是基本数据类型,
==
比较的是值,若是引用数据类型,==
比较的是引用地址 equals
未被重写,等价于==
,若被重写了,则比较的是值
hashCode( ) 与 equals( )
两者都是用于比较两个对象是否相等,但hashCode效率更高
- 两个对象的hashCode值相等,但两个对象不一定相等(hash碰撞)
- 两个对象的hashCode值相等并且equals()方法返回true,两个对象才相等
- 两个对象的hashCode值不相等,那两个对象一定不相等
Object中常见的方法
- hashCode( )
- equals( )
- wait( )
- notify( ) , notifyAll( )
- toString( )
- finalize( )
final ,finally,finalize三者区别
- final是一个关键字,用于修饰不可变的变量、方法和类
- finally也是一个关键字,用于定义必须执行的代码块,在和try-catch一起使用,无论是否捕获或处理异常,
finally
块里的语句都会被执行。当在try
块或catch
块中遇到return
语句时,finally
语句块将在方法返回之前被执行。注意:finally块中的return会覆盖try里面的return - finalize是Object的一个方法,用于完成对象在销毁前必须完成的操作,在垃圾回收前被调用。
String StringBuilder和StringBuffer
- String是不可变的(String类中字符数组被final修饰且为私有的)
- StringBuilder 可变,线程不安全
- StringBuffer 可变,线程安全的
字符串拼接使用
+
,实际上是通过StringBuilder调用append()
方法实现的,拼接完成后调用toString()方法得到String对象
在循环中,+
拼接字符串会导致每次都创建StringBuilder对象,不过在JDK9中这个问题得到解决
序列化与反序列化
- 序列化:将数据结构或对象转换为二进制字节流
- 反序列化:将二进制字节流转换为数据结构或对象
不想进行序列化的变量用transient
修饰
注意:
transient
只能修饰变量,不能修饰类和方法transient
修饰的变量,在反序列化后会被设置成该类型的默认值static
变量不需要用transient
修饰也不会被序列化
反射
通过反射可以获取任意一个类的所有属性和方法,并且可以调用这些属性和方法
获取Class对象的四种方式
-
知道具体类
Class alunbarClass = TargetObject.class;
-
知道类的全路径
Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
-
通过对象实例
TargetObject o = new TargetObject(); Class alunbarClass2 = o.getClass();
-
通过类加载器传入类路径
ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
通过反射操作类的方法和参数
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
/**
* 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例
*/
Class<?> targetClass = Class.forName("cn.javaguide.TargetObject");
TargetObject targetObject = (TargetObject) targetClass.newInstance();
/**
* 获取 TargetObject 类中定义的所有方法
*/
Method[] methods = targetClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
/**
* 获取指定方法并调用
*/
Method publicMethod = targetClass.getDeclaredMethod("publicMethod",
String.class);
publicMethod.invoke(targetObject, "JavaGuide");
/**
* 获取指定参数并对参数进行修改
*/
Field field = targetClass.getDeclaredField("value");
//为了对类中的参数进行修改我们取消安全检查
field.setAccessible(true);
field.set(targetObject, "JavaGuide");
/**
* 调用 private 方法
*/
Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
//为了调用private方法我们取消安全检查
privateMethod.setAccessible(true);
privateMethod.invoke(targetObject);
}