JavaSE学习笔记 - 面向对象基础
面向对象
- 面向对象特点:封装性、继承性、多态性、(抽象性)
- 面向对象:不关心具体的步骤,具体的步骤已经由某一个对象完成,考虑哪一个对象完成了这个事情,找到这个对象来做某一件事情。
- 面向过程:需要考虑具体的过程,要自己考虑每一步的实现过程,考虑怎么去做。
- 类和对象:创建类,设计类的属性;创建类的对象;通过 "对象.属性","对象.方法"调用对象的结构,功能。
- 万物皆对象:在 Java 语言范畴中,我们将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构。(Scanner,String,File,URL),涉及到Java语言与前端 HTML、后端数据库交互的时候,前后端的结构在Java层面交互的时候都体现为类和对象。
JVM内存结构
- 编译完源程序以后,生成字节码文件;使用 JVM 中的类加载器和解释器对生成的字节码文件进行解释运行。需要将字节码文件对应的类加载到内存中,涉及到内存解析。内存解析是在运行的时候进行的。
方法的使用
- 方法重载(OverLoad):对于功能类似的方法来说,参数列表不一样,可以使用相同的方法名,多个方法的名称一样,但是参数列表不同,使用一个方法名称,就可以实现相似的多个功能。
- 可变个数形参的格式:数据类型...变量名,可变参数必须声明在形参的末尾。
- 方法递归:方法自身调用自身,使用递归的时候必须添加结束条件,如果没有结束条件将会反生栈溢出。 StackOverflowError,当没有递归出口的时候,方法就会被一直压栈,直到栈内存被全部占满。
递归调用内存图
方法参数的值传递
- 如果变量是基本数据类型,赋值的时候就是变量保存的数据值。如果变量是引用数据类型,赋值的时候就是变量保存的地址值。
- 如果变量作为参数进行值传递,此时在方法中这个值是形参。如果参数是基本数据类型,此时实参给形参的是实参的真实数据值。如果参数是引用数据类型,此时实现给形参的是实参的存储数据的地址值。
代码块
- 作用:跟普通方法一样,完成某一个功能的
- 写法:可以认为程序块是一个 没有修饰符 没有参数 没有返回值 没有名字的特殊方法
- 用法:块也需要调用才能执行 我们自己调用不到(没有名字) 每一次我们调用构造方法之前 系统会帮我们自动的调用一次程序块 让他执行一遍
- 特点:没有什么重载的概念(名字,参数都没有,但是可以在类中定义 多个程序块)
- 块可以在里面写一些程序,在创建对象之前执行
//在运行的时候自动执行
public class Main {
public static void main(String[] args) {
{
System.out.println(5);
}
}
}
this关键字
- 构造方法中,属性与变量名重名的时候,this 表示当前对象,是一个关键字(指代词)代替的是某一个对象(当前调用属性或者方法时的那个对象)
- 调用属性或者方法这一行代码可以放置在类中的任何成员位置 上下顺序随意
- this可以调用构造方法:构造方法,一般方法,有顺序,认为构造方法早于一般方法,一般方法内不能调用构造方法,构造方法可以重载,在有参构造方法中调用无参构造方法 this() 省略了构造方法的名字,必须与类名一致,方法之间来回的调用是不可以的。
public class Person {
int no;
String name;
public Person() {
System.out.println("无参构造方法执行了...");
}
public Person(int no, String name) {
this();
this.no = no;
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person(2017, "张三");
System.out.println(person);
}
}
static关键字
static在属性上:类属性,静态属性
- 类属性的访问可以通过类名.类属性,还可以通过对象.类属性访问。类属性的当前的字节码文件加载进 JVM 的时候,类属性就会被初始化,JVM 执行完毕字节码文件才销毁。
- 类属性可以被每一个对象共享,类方法初始化很早,JVM 加载这个类的时候这个方法就初始化了,对象方法也是很早就进入 JVM,但是不一定被启用了,只有先创建了对象才能调用方法。
- 静态变量对于所有的对象来说都共享一个静态变量,而非类中静态变量对于每一个对象都有单独的一份。
static在方法上:类方法(作为工具类使用)
- 类方法不能访问对象的属性,类方法加载的比较早,也不能调用对象方法。类方法只能访问类属性和类方法。
- 对象方法内可以访问类方法
静态代码块
- 主要用于初始化资源;静态代码块在 main 之前执行,因为静态代码块在类加载的时候就执行了;不能访问对象属性和对象方法,静态方法和静态代码块没有顺序;一个类中可以写多个静态代码块,这些代码块遵循自上而下的顺序依次执行。(如果想要在类进行加载的时候就执行某些代码,那么可以使用静态代码块。)
单例模式 *
类加载机制、字节码知识、JVM 指令重排、Java 序列化机制
一个类只能创建一个实例,设计一个类只能有一个对象,不能创建第二个对象。(线程池,数据库连接池)
实现步骤
- 私有化构造器
- 定义一个类方法用于获得单例的对象,返回值是这个类的类型
- 在类中提供一个私有的 Singleton 类型的类属性(静态属性)
- 实现 getInstance 这个方法
实现方式
- 懒汉模式:使用的时候再进行初始化,延迟加载。
public class Main {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton);//cn.edu.zut.Singleton@1b6d3586
System.out.println(singleton1);//cn.edu.zut.Singleton@1b6d3586
}
}
//懒汉模式
class Singleton {
//只能有一个实例
private static Singleton singleton;
//私有化构造方法
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
//收到编译器的影响,可能会先执行3,在执行2,指令集进行重新排序,加上volatile(多线程编程)
//首先在堆空间分配一个空间,然后在栈空间赋值一个引用,分配空间的时候会返回一个该空间的内存引用
//初始化空间
//把内存引用赋值给singleton
singleton = new Singleton();
}
return singleton;
}
}
- 饿汉模式:在类加载的时候就完成了实例的初始化。类加载:加载对应的二进制文件,在方法区,创建对应的数据结构;连接,1.验证,2.准备(给变量初始值),3.解析;初始化:给静态属性赋值。(通过类加载机制,来保证线程安全。)
public class Main {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton);//cn.edu.zut.Singleton@1b6d3586
System.out.println(singleton1);//cn.edu.zut.Singleton@1b6d3586
}
}
//饿汉模式
class Singleton {
//只能有一个实例
private static Singleton singleton = new Singleton();
//私有化构造方法
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
- 静态内部类:延迟加载
public class Main {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton);//cn.edu.zut.Singleton@1b6d3586
System.out.println(singleton1);//cn.edu.zut.Singleton@1b6d3586
}
}
//静态内部类:延迟加载
class Singleton {
static class InnerClass {
private static Singleton singleton = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
//在调用这个的时候,InnerClass会进行加载
return InnerClass.singleton;
}
}
- 反射攻击:通过反射机制来创建不同的实例
public class Main {
public static void main(String[] args) throws Exception {
Constructor<Singleton> constructor
= Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton singleton = constructor.newInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton);//cn.edu.zut.Singleton@1b6d3586
System.out.println(singleton1);//cn.edu.zut.Singleton@4554617c
}
}
class Singleton {
static class InnerClass {
private static Singleton singleton = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
//在调用这个的时候,InnerClass会进行加载
return InnerClass.singleton;
}
}
- 枚举类型:使用反射机制的时候会直接抛出异常,枚举类型不支持反射创建。
public class Main {
public static void main(String[] args) throws Exception {
Enum singleton = Enum.SINGLETON;
Enum singleton1 = Enum.SINGLETON;
System.out.println(singleton);//SINGLETON
System.out.println(singleton1);//SINGLETON
System.out.println(singleton.hashCode());//460141958
System.out.println(singleton1.hashCode());//460141958
Constructor<Enum> constructor = Enum.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
Enum singleton2 = constructor.newInstance("SINGLETON", 0);
//java.lang.IllegalArgumentException: Cannot reflectively create enum objects
//不能对枚举类型进行实例化
System.out.println(singleton2);
}
}
enum Enum {
SINGLETON;
// 0: new #4 // class cn/edu/zut/Enum 分配空间
// 3: dup
// 4: ldc #7 // String SINGLETON 加载实例
// 6 : iconst_0
// 7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V 调用构造方法
// 10: putstatic #9 // Field SINGLETON:Lcn/edu/zut/Enum; 对静态属性进行赋值
// 13: iconst_1
// 14: anewarray #4 // class cn/edu/zut/Enum
// 17: dup
// 18: iconst_0
// 19: getstatic #9 // Field SINGLETON:Lcn/edu/zut/Enum;
// 22: aastore
// 23: putstatic #1 // Field $VALUES:[Lcn/edu/zut/Enum;
// 26: return
}
- 序列化
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Singleton singleton = Singleton.getInstance();
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton"));
oos.writeObject(singleton);
oos.close();
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton"));
Object obj = ois.readObject();
Singleton singleton1 = (Singleton) obj;
// System.out.println(singleton1);//cn.edu.zut.Singleton@378bf509
// System.out.println(singleton);//cn.edu.zut.Singleton@7f31245a
//添加Object readResolve() throws ObjectStreamException,接口中的替代方法,防止反序列化攻击
//枚举类型也可以防止反序列化攻击
System.out.println(singleton1);//cn.edu.zut.Singleton@7f31245a
System.out.println(singleton);//cn.edu.zut.Singleton@7f31245a
}
}
class Singleton implements Serializable {
private static final long serialVersionUID = -8122532461435652822L;
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
Object readResolve() throws ObjectStreamException {
return singleton;
}
}