面向对象的三大特性
- 封装
隐藏类的实现细节,保证属性的安全有效。具体实现方式:私有化属性,提供共有的访问属性的方法(一般为get/set方法),构造中调用方法。
- 继承
继承是代码复用的一种方式,子类可以继承父类的所有属性(包括私有的)和部分方法(不能继承父类私有方法和构造方法)。
java只支持单继承,但是可以通过接口的方式多实现。
继承是重量级的代码复用方式,一般通过聚合方式(类中持有对象)复用代码。
- 多态
java中,多态就是指一个名称的多种形态,包括方法的多态(重写,重载)和引用的多态。一般的多态,指的是引用的多态。
满足多态的三个必要条件:子类继承父类,父类引用指向子类对象,重写父类方法。
方法的重载和重写
- 重载
重载发生在同一个类中。
规则:方法名相同,返回值尽量相同,参数列表不同(参数的数量,参数的顺序,参数的类型不同)。
- 重写
重写必须发生在父子类之间。
规则:方法名相同,返回值相同(也可以返回的是子类),参数列表相同。访问全向不能变小,抛出的异常不能变大。
this、static、final、abstract关键字
- this
概念:对于构造方法来说,this代表当前正在构造的对象;对于普通方法来说,this代表当前正在调用的对象。
使用方式:
1、当属性与参数名称相同,使用this修饰属性,明确告知编译器哪一个是成员变量,哪一个是参数名称。
2、构造方法的第一行(必须是第一行,否则编译报错),使用this(参数)来调用本类其它构造方法。
- static
static修饰的成员属性,由对象层级提升到类层级,被所有的对象共享。属性不再随着对象的创建而创建,而是随着类的加载而创建。
static修饰的方法,可以直接使用(类名.方法名)调用
- final
由final修饰的类不可被继承,方法不可被重写,成员变量不可再改变(不可二次赋值,必须赋值一次)。
final修饰的属性,不是常量,只是针对同一个对象,该属性不能被更改而已;只有加了static final修饰的属性才是常量。
- abstract
abstract可以修饰类和方法。其修饰的类不能被实例化;其修饰的方法不在其声明的类中实现,但是必须在某个子类中重写。
abstract注释
采用 abstract 方法的类本来就是抽象类,并且必须声明为 abstract。
abstract 类不能实例化。
仅当 abstract 类的子类实现其超类的所有 abstract 方法时,才能实例化 abstract 类的子类。这种类称为具体类,以区别于 abstract 类。
如果 abstract 类的子类没有实现其超类的所有 abstract 方法,该子类也是 abstract 类。
abstract 关键字不能应用于 static、private 或 final 方法,因为这些方法不能被重写,因此,不能在子类中实现。
final 类的方法都不能是 abstract,因为 final 类不能有子类。
抽象类和接口的区别
类的定义方式不同,抽象类用class,接口用interface
继承方式不同,抽象类是单继承,使用关键字extends;接口是多实现的,使用关键字implements。
接口中的属性,必须是public修饰的常量;抽象类随意。
抽象类中可以有抽象方法,也可以没有;接口中的方法一定是public abstract的。抽象类一定有构造方法,但是不能new对象,该构造是为了子类的调用;接口中没有构造方法的存在。
接口具有强制性,其方法必须被实现类重写;抽象类没有。
定义规范:最好使用接口。
抽象类的实际作用
抽象类的作用不在于创建对象(也不能创建),而在于被继承。
其子类必须重写父类的所有抽象方法,否则也会被定义为抽象类。可以说,抽象类,对其实现类的代码提供了强制性和规范性(模板设计模式便是源于此)。
单例设计模式
/**
* 单例设计模式(饿汉式\饱汉式)
*
*/
public class TestSingleton {
// private static TestSingleton ts = new TestSingleton();
private static TestSingleton ts ;//2.持有本身对象
private TestSingleton(){//1.私有化构造
}
// public static TestSingleton getSingleton(){
// return ts;
// }
public static TestSingleton getSingleton(){//3.提供公开方法,以获取单例对象
ts = new TestSingleton();
return ts;
}
}
class Test{
public static void main(String[] args) {
TestSingleton singleton = TestSingleton.getSingleton();//获取单例对象
}
}
JVM加载类的三种方式以及区别
三种方式
运行时通过new方式载入。
如:Dog dog = new Dog();通过反射加载类,并创建对象实例。
如: Class clazz = Class.forName(“Dog”); Object dog = clazz.newInstance();调用某个ClassLoader实例的loadClass()方法。
如:Class clazz = ClassLoader.loadClass(“Dog”); Object dog = clazz.newInstance();
区别
第一种和第二种方式使用的类加载器是相同的,都是当前类的类加载器( this.getClass().getClassLoader() );第三种方式用的类加载器是用户指定的,其加载的类与当前类加载器加载的类属于不同命名空间。
第一种是静态加载;第二种和第三种是动态加载。
Class.forName与ClassLoader.loadClass区别
Class的装载包括3个步骤:加载(loading),连接(link),初始化(initialize).
Class.forName(className)实际上是调用Class.forName(className, true, this.getClass().getClassLoader())。第二个参数,是指Class被loading后是不是必须被初始化。
ClassLoader.loadClass(className)实际上调用的是ClassLoader.loadClass(name, false),第二个参数指Class是否被link。
Class.forName(className)装载的class已经被初始化,而ClassLoader.loadClass(className)装载的class还没有被link。一般情况下,这两个方法效果一样,都能装载Class。但如果程序依赖于Class是否被初始化,就必须用Class.forName(name)了。
例如,在JDBC编程中,常看到这样的用法,Class.forName(“com.MySQL.jdbc.Driver”).如果换成getClass().getClassLoader().loadClass(“com.mysql.jdbc.Driver”),就不行。
com.mysql.jdbc.Driver的源代码如下:// Register ourselves with the DriverManager static { try { Java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
原来,Driver在static块中会注册自己到java.sql.DriverManager。而static块就是在Class的初始化中被执行。
所以这个地方就只能用Class.forName(className)。对于相同的类,JVM最多会载入一次。但如果同一个class文件被不同的ClassLoader载入,那么载入后的两个类是完全不同的。因为已被加载的类由该类的类加载器实例与该类的全路径名的组合标识。设有 packagename.A Class ,分别被类加载器 CL1 和 CL2 加载,所以系统中有两个不同的 java.lang.Class 实例:
对象实例化四种方式:
new Dog();
clazz.newInstance();或者 clazz.getConstuctor(…).newInstance(…);
Object.clone();//通过本地方法进行克隆
反序列化
对象的创建过程
JVM去磁盘中寻找class文件,读入内存。
进行类的加载,先加载父类再加载子类;同时分配static属性的内存空间,执行static语句块。
给父类和子类的非静态属性分配内存空间,清空属性或清0.
执行父类的属性初始化语句(如果有属性有赋值那么就初始化该值,否则还是空或者0);
执行父类的语句块
执行父类构造
执行子类的属性初始化语句(如果有属性有赋值那么就初始化该值,否则还是空或者0);
执行子类的语句块
执行子类构造