1. 面向对象的三大特性
1) 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
2) 继承,是指可以让某个类型的对象获得另一个类型的对象的属性和方法。
3) 多态,是指一个类实例的相同方法在不同情形有不同表现形式。
2. 重载重写
1) 重载是在同一个类中的两个或两个以上的方法,拥有相同的方法名,但是参数却不相同,方法体也不相同,最常见的重载的例子就是类的构造函数。
两同一不同原则:
两同:同一个类,方法名相同;
不同:参数列表不同;
注意:方法的其它部分,比如方法的返回值类型、方法修饰符等都与方法重载没有任何关系。
2) 重写是子类的方法覆盖父类的方法,要求方法名和参数都相同。
两同两小一大原则:
两同:方法名相同、形参列表相同;
两小:子类方法返回值类型小于父类方法返回值类型或相等;子类方法声明抛出的异常类型应比父类方法声明抛出的异常类型更小或相等。
一大:子类方法的访问权限要大于等于父类方法的访问权限。
注意: private修饰的相同方法不是被重写了,而是另一个重新定义的方法,因为private只对当前类可见。
3.常用Object类的方法
equals()方法
== 运算符
对于基本类型变量,如果两者数值相同,则返回true。
对于引用类型变量,只有两者指向同一个对象,才返回true。
Object默认提供的功能只是比较两个对象的地址是否相同。通常需要重载equals()方法。
例如:在String类中就重载了equals()方法,用来判断String对象包含的字符序列是否相同。
//比较严谨的重载equals()方法的实例如下
public boolean equals(Object obj)
{
//如果两个对象为同一个对象
if (this == obj) {
return true;
}
//getClass()获得的是对象的运行时类型
if (obj != null && obj.getClass() == Person.class)
{
Person personObj = (Person)obj;
//并且当前对象的idStr 与 obj对象的idStr 相等的时候才可以判断两个对象是相等的
if (this.getIdStr().equals(personObj.getIdStr())
{
return true;
}
}
return false;
}
toString()方法
通常在打印对象信息时会使用到toString()方法,比如:
System.out.println(p);
//等价于
//输出:Person@15db9742
//打印格式为:类名+@+hashCode
System.out.println(p.toString);
通常需要重写toString()方法,该方法总是会返回该对象的所有令人感兴趣的信息所组成的字符串,可以返回如下格式的字符串:
类名[field1=值1, field2=值2,...]
hashCode()方法
hashCode()方法主要在集合中才会用到。
将对象放入集合中的步骤:
1) 首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。
2) 如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。
finalize()方法
在垃圾回收机制回收某个对象占用的内存之前,需要程序调用一定的方法来清理资源。而java提供了默认的机制来清理该对象的资源,该机制就是finalize()方法。
注意: 垃圾回收机制什么时候调用对象的finalize()方法是完全透明的,只有当程序认为需要更多的额外内存空间时,它才能够进行垃圾回收的工作。也就是说finalize()方法是在垃圾回收之前执行的。
比如: 当一个对象虽然失去了引用,但是只占用了少量的内存资源,而且系统没有很严重的内存需求,此时,垃圾回收机制没有试图去回收对象所占用的资源,这样对象的finalize()方法是不会被调用的。
finalize()方法4个特点:
1) 永远不要主动调用某个对象的finalize()方法,该方法应交给垃圾回收机制调用。
2) finalize()方法何时被调用,是否被调用具有不确定性,不要把finalize()方法当成一定会被执行的方法。
3) 当JVM执行可恢复对象的finalize()方法时,可能使该对象或系统中其他对象重新变成可达状态。
4) 当JVM执行finalize()方法时出现异常时,垃圾回收机制不会报告异常,程序继续执行。
getClass()方法
返回当前对象所属类对应的CLASS对象(具体方法查看API);
clone()方法
用来帮助其他对象来实现“自我克隆”,也就是得到一个当前对象的副本,而且两者完全隔离。Object类提供的clone()方法使用了protected修饰符,该方法只能够被子类重写或调用。
注意: Object类中提供的Clone机制只可以对对象里面的各实例变量进行“简单复制”,如果实例变量的类型为引用类型,Object的Clone机制也只能够简单地复制这个引用变量。
参考:Java疯狂讲义 P247页
4. 类访问权限
修饰词 | 本类 | 同一个包的类 | 继承类 | 其他类 |
---|---|---|---|---|
private | √ | × | × | × |
无(默认) | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
5. String、stringbuffer、stringbuilder 联系、区别、源码
String:字符串常量
StringBuffer:字符串变量(线程安全)
StringBuilder:字符串变量(线程非安全)
三者在执行速度方面的比较:StringBuilder > StringBuffer > String
使用场景:
1) 如果要操作少量的数据用 = String
2) 单线程操作字符串缓冲区下操作大量数据 = StringBuilder
3) 多线程操作字符串缓冲区下操作大量数据 = StringBuffer
为什么线程安全比非线程安全性能低?
1) 线程的安全是以牺牲性能为代价的,所谓线程安全就是多了个加锁,解锁的操作,比如100亿个操作中都要加锁和解锁,线程是安全了,但性能就下降了。
2) 有些软件是以性能为主的,为了提高性能,就少了加锁,解锁的操作,虽然容易出现并发性问题,但性能却提高了。
6. final修饰符
final修饰的变量不可改变。
final修饰成员变量
说明:final修饰的成员变量必须由程序员显示地指定初始值。
类变量:必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定。
实例变量:必须在非静态初始化块、声明该变量或构造器中指定初始值,而且只能在三个地方的其中之一指定。
final修饰局部变量
final修饰局部变量在定义时没有指定默认值,可以在后面代码中对该final变量赋初始值,但是只能一次,不可重复赋值。
final修饰的形参不能在方法体内赋值,因为形参在调用该方法时,是由系统根据传入的参数来完成初始化的。
final修饰基本类型变量和引用类型变量
final修饰基本类型变量时,不可以对基本类型变量重新赋值,因此基本类型变量不能被改变。
final修饰引用类型变量时,只保证该引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
final修饰的方法
final修饰的方法不能被重写,并不是不能被重载。(例如Object类中的getClass()方法就是一个final方法)
final修饰类
final修饰的类不可以有子类
immutable不可变类:当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。
7. 抽象类和接口的联系区别
共同点:
1) 接口和抽象类都不能被实例化,它们都在继承树的顶端,用来被其他类实现和继承。
2) 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。
设计目的:
接口主要体现一种规范,它类似于整个系统的“总纲”,它制定了系统各模块应该遵循的标准,因此一个系统中的接口不应该经常改变。
抽象类主要体现了一种模板式设计。可以认为是系统实现过程当中的中间产品。它已经实现了系统的部分功能(哪些已经提供实现的方法)
区别:
1)接口中只能包含抽象方法、静态方法和默认方法,不能为普通方法提供方法的实现;而抽象类则完全可以包含普通方法。
2)接口中只能定义静态常量,不能定义普通成员变量;抽象类里则可以定义普通成员变量,也可以定义静态常量。
3)接口里不包含构造器;抽象类里可以包含构造器;但是抽象类中的构造器不是用来创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
4)接口中不可以包含初始化块;但是抽象类中可以包含初始化块。
5)一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补java单继承的不足。
什么时候用接口什么时候用抽象类?
抽象类都是从一些相似的对象中抽象出来的一个相对无法具体描述的一个类,它的子类之间是有相似性的;
接口更侧重于对相同的动作进行抽象封装。当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
8. 对象的初始化过程
1)初始化父类中的静态成员变量和静态代码块;
2)初始化子类中的静态成员变量和静态代码块;
3)初始化父类的普通成员变量和代码块,再执行父类的构造方法;
4)初始化子类的普通成员变量和代码块,再执行子类的构造方法;
9. static修饰符
static修饰变量
1)对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存。
2)对于实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。
static方法
静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法和变量的
static代码块
static代码块是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。
关键的作用:用来形成静态代码块以优化程序性能。因为它只会在类加载的时候执行一次,所以能优化程序性能。
10. instanceof运算符
编译时类型必须是如下3种情况:
1)要么与后面的类相同;
2)要么是后面类型的父类;
3)要么是后面类型的子类;
如果前面操作数的编译时类型与后面的类型没有任何关系,程序将没发通过编译。
运行阶段:
被转型变量所引用对象的实际类型必须是目标类型的实例,或者是目标类型的子类,实现类的实例,否则在运行时将引发ClassCastException异常。