文章目录
1. 对象的概念
Java 是面向对象的编程语言,对象就是面向对象程序设计的核心。**所谓对象就是真实世界中的实体,对象与实体是一一对应的,也就是说现实世界中每一个实体都是一个对象,它是一种具体的概念。**对象有以下特点:对象具有属性(数据)和行为(方法)。
- 对象具有变化的状态。
- 对象具有唯一性。
- 对象都是某个类别的实例。
- 一切皆为对象,真实世界中的所有事物都可以视为对象。
2. 类的创建方式
(1)new关键字创建对象
Object a=new Object();
在创建a这个对象时,jvm底层会做这些事:
- java的new关键字是用于创建对象的,它的原理是在堆内存中分配一块内存空间来存储对象的属性和方法,在java中所有的对象必须都是通过new创建的
- 当使用new关键字创建一个对象时,JVM虚拟机会首先检查是否有足够的内存空间来存储对象的属性和方法。如果有足够的内存空间,Java虚拟机会为对象分配内存,然后调用对象的构造函数来初始化对象的属性和方法
- 构造函数是一种特殊的方法,它用于初始化对象的属性和方法,当使用new创建一个对象时,java虚拟机会根据对象的类型来调用相应的构造方法。构造方法可以有多个重载的版本,每个版本可以接受不同类型和参数
- 在调用构造方法之前,Java虚拟机会为对象先分配内存,并将内存空间中的每个字节都初始化默认值。然后,Java虚拟机会将对象的引用传递给构造方法,构造方法可以使用这个引用访问对象的属性和方法
(2)使用反射创建对象
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。(反射机制可以打破类成员的作用域)
用Class对象的newInstance()方法制作对象
Class<User> user=User.class;
// Computer computer = new Computer();
// Class<? extends Computer> aClass = computer.getClass(); 表示这个类是computer或其子类
// Class.forName("com.dmdd.ioc.Computer"); 根据全限定名获取反射对象
User user1 = user.newInstance();//会默认调用无参数构造函数
(3)调用对象的 clone() 方法
new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,根据类型分配内存,再调用构造函数,填充对象的各个域,这一步就叫对象的初始化。初始化完毕后,可以把他的引用(地址)发布到外部,在外部就可以通过引用操纵这个对象。clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和源对象一样,然后再使用源对象中对应的各个域,填充新对象的域。同样可以可以把这个新对象的引用发布到外部 。(clone方法其实实现了对象的深拷贝)
Computer computer = new Computer();
Computer computer2=computer.clone();
3. static关键字
在Java中,static表示“静态的”,它也是一种修饰符,可以修饰属性、方法、代码块和内部类。static修饰符具有如下特性:
● 被static修饰的属性(成员变量)称为静态变量,也叫做类变量;
● 被static修饰的常量称为静态常量;
● 被static修饰的方法称为静态方法,也叫做类方法;
● 被static修饰的代码块叫做静态代码块;
● 被static修饰符的内部类,叫做静态内部类。
静态变量、静态方法、静态常量统称为类的静态成员,归整个类所有,不属于某个单一的对象。也就是说,静态成员不属于某个对象单独拥有,而是被类的所有实例对象共享。
- 静态变量和实例变量
- JVM虚拟机只会为静态变量分配一次内存,在加载类的过程中完成对静态变量的内存分配;
- 我们可以在类的任意方法中直接访问任意静态变量;(非静态方法或静态方法都可以直接访问静态变量)
- 我们可以在其他类中通过"类名.静态变量"的形式,来访问该类中的静态变量。
- 每创建一个Java实例对象,JVM虚拟机就会为该实例变量分配一次内存;
- 我们可以在类的非静态方法中直接访问实例变量;
- 在类的静态方法中,需要通过"对象.实例变量"的形式进行访问。
- 静态方法
被static修饰的方法叫做静态方法,也叫做类方法。我们经常在工具类中定义静态方法,比如常用的工具方法Arrays.sort()、Math.random()等。静态方法具有以下特性:
- 静态方法中只能调用静态成员和静态方法;
- 静态方法中不能使用this和super关键字;
- 抽象方法不能使用static进行修饰;
- 静态方法不能被重写,但可以被继承!
- 静态代码块
静态代码块(Static Block)是Java中类初始化的一部分,用于在类加载时执行一些特定的初始化操作。静态代码块是在类的加载阶段执行的,只会执行一次,无论创建多少个类的实例。(它本质上是一种初始化机制)
public class MyClass {
static {
// 静态代码块中的初始化操作
}
}
如果一个类包含多个静态代码块,它们将按照它们在类中的出现顺序依次执行。这可以用来实现复杂的初始化操作,确保初始化按照特定的顺序进行。
静态代码块的主要用途如下:
- 加载驱动程序:在数据库编程中,通常在静态代码块中加载数据库驱动程序。
- 静态字段初始化:可以在静态代码块中初始化静态字段,确保它们在类加载时具有合适的初始值。
- 执行复杂初始化逻辑:当类的初始化涉及到复杂的逻辑,需要确保只执行一次时,静态代码块可以派上用场。
4. final关键字
final 是 Java 中的关键字,用于修饰类、方法、字段以及局部变量,具有不同的含义和作用,以下是 final 关键字的详细解释:
- final修饰类
当一个类被声明为 final 时,意味着该类不能被继承,即它是不可被子类化的。这通常用于确保类的完整性和安全性,防止其他类继承并修改其行为。
final class FinalClass {
// ...
}
- final修饰方法
当一个方法被声明为 final 时,意味着该方法不能被子类重写,即它是不可被覆盖的。这可以用于确保方法的行为在子类中不会被修改
class BaseClass {
final void finalMethod() {
// ...
}
}
- final修饰字段
当一个字段被声明为 final 时,意味着该字段的值只能被赋值一次,通常在声明时或在构造函数中赋值,如果final修饰的字段是一个对象的引用,那么表示这个引用不会变,但所引用的对象是可以变的。final 字段是不可变的,即它的值不能被修改。(这用于创建常量或确保字段的不变性)
final 关键字用于不同的上下文,它可以用于类、方法、字段和局部变量,具有不同的含义和作用。在类级别,它可以用于防止继承或确保类的完整性;在方法级别,它可以用于防止方法被重写;在字段级别,它可以用于创建常量或确保字段的不变性;在局部变量级别,它可以用于确保局部变量的不变性
5. 内部类
java内部类分为: 成员内部类、静态嵌套类、方法内部类、匿名内部类 。内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号 。内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的 。内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量 。
- 成员内部类
class Outer {
class Inner{}
}
编译上述代码会产生两个文件:Outer.class
和Outer$Inner.class。
- 方法内部类
class Outer {
public void doSomething(){
class Inner{
public void seeOuter(){
}
}
}
}
方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。方法内部类对象不能使用该内部类所在方法的非final局部变量。
因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。从 Java 8 开始,你可以省略局部变量的final关键字,只要这些变量在其生命周期内没有发生变化(实际上是隐式的final)。这是因为 Java 编译器可以自动检测这些变量是否满足不可更改的条件,而无需显式地将它们声明为final。这个特性使代码更加简洁,但仍然遵循了局部内部类对象不能引用变化的局部变量的原则。
- 匿名内部类
匿名内部类(Anonymous Inner Class)是 Java 中的一种特殊类型的内部类,它允许你在使用时直接定义和实例化一个类,而不需要为其单独创建一个命名的类。匿名内部类通常用于创建临时的、只需在一个地方使用的类实例。
interface MyInterface {
void doSomething();
}
MyInterface anonymousInnerClass = new MyInterface() {
@Override
public void doSomething() {
// 实现接口方法
}
};
- 特点
- 匿名内部类通常是针对接口或抽象类的实例化,用于提供实现。
- 匿名内部类没有类名,它是一个临时的、匿名的内部类。
- 匿名内部类通常在创建对象的地方定义和使用,不需要单独的类声明。
- 原则:
- 匿名内部类不能有构造方法:匿名内部类没有类名,因此无法显式定义构造方法。它会隐式调用父类的构造方法,通常是默认的无参数构造方法。
- 匿名内部类不能定义任何静态成员、静态方法:静态成员和静态方法属于类而不是实例。匿名内部类没有类名,因此无法定义静态成员或静态方法。
- 匿名内部类不能是public,protected,private,static:匿名内部类是一个局部类,它的作用域限定在创建它的方法内部。因为它的作用范围有限,所以不允许设置访问修饰符,也不能使它成为静态的。。
- 只能创建匿名内部类的一个实例:匿名内部类通常用于一次性任务,创建一个实例后,它通常不会再次被使用。因为匿名内部类没有类名,所以不能直接用类名创建多个实例。
- 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类:匿名内部类通常用于实现接口或继承抽象类,以提供具体的实现。它通过创建一个继承自某个类或实现某个接口的匿名子类来实现这一点。
- 因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效:匿名内部类本质上就是局部内部类,因此继承了局部内部类的所有限制。这些限制包括作用范围受限、不允许设置访问修饰符等。
总之,匿名内部类的限制是为了适应它的用途和特性,即提供一种简洁的方式来创建临时实现和处理一次性任务,而不是为了创建可复用和复杂的类。匿名内部类在简化代码的同时,也有一些固有的局限性。如果需要更灵活的类定义,通常应该使用普通的命名内部类。
- 静态内部类
静态内部类被称为内部类,但它的名称可能会引起一些误解。虽然静态内部类的定义确实出现在外部类的内部,但它与外部类的实例无关,不需要外部类的实例来创建静态内部类的对象。实际上,静态内部类更像是嵌套在外部类中的普通类,它只是由于在外部类内定义而被称为内部类。虽然静态内部类与外部类无关,但它仍然在外部类的命名空间中,这意味着它可以访问外部类的静态成员。这是静态内部类与普通类不同的地方,它可以共享外部类的命名空间,但不依赖于外部类的实例。
public class OuterClass {
private static int outerStaticField;
private int outerInstanceField;
static class StaticInnerClass {
private static int innerStaticField;
private int innerInstanceField;
public void doSomething() {
// 静态内部类可以访问外部类的静态字段
System.out.println(outerStaticField);
// 静态内部类不能访问外部类的实例字段
// System.out.println(outerInstanceField);
}
}
}
为什么需要内部类:
典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其他外围类的对象。所以你可以认为内部类提供了某种进入其外围类的窗口。使用内部类最吸引人的原因是:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
6. super和this关键字
在了解这个两个关键字之前我们首先了解一下java的内存模型(JMM)
class Chiniese{
String name;
}
class Main{
public void nihao(){
}
上面代码在内存中的模型如下
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造方法调用时,一定先调用父类的构造方法。所以,super :代表父类的存储空间标识(可以理解为父亲的引用)。 this :代表当前对象的引用(谁调用就代表谁)。
7. Object类
java.lang.Object 类是 Java 标准库中的根类(Root Class),它是所有类的直接或间接父类。它的核心源码如下:
public class Object {
//构造函数
public Object() {}
//返回对象的运行时类,即对象所属的类,这是个native方法是由c++实现
@IntrinsicCandidate
public final native Class<?> getClass();
//返回一个对象的hash码,java对象的hash码会写到对象头中,它是一种懒加载的形式,除非真正调用hashCode方法,java的对象头是不会生成hash码的,它也是个native方法
@IntrinsicCandidate
public native int hashCode();
//equals方法用于比较对象是否相等。默认实现是比较对象的引用是否相同。通常我们需要重写这个方法
public boolean equals(Object obj) {
return (this == obj);
}
//对象的深拷贝
@IntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
//tostring方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
//唤醒等待在该对象上的一个线程。
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
//唤醒等待在该对象上的一个线程所以线程。
@IntrinsicCandidate
public final native void notifyAll();
//导致当前线程等待,直到另一个线程调用 notify() 或 notifyAll() 方法。
public final void wait() throws InterruptedException {
//wait(0)表示无限等待
wait(0L);
}
public final native void wait(long timeoutMillis) throws InterruptedException;
}
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("timeoutMillis value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) {
timeoutMillis++;
}
wait(timeoutMillis);
}
//该方法用于垃圾收集时方法用于自救
@Deprecated(since="9")
protected void finalize() throws Throwable { }
8. Class类
class 类实际上是描述 Java 类结构的一种数据结构,它用于加载、解析、验证、存储类文件的相关信息,并支持类的实例化和方法的调用。在 JVM 中,class 对象的创建和管理是由类加载器和运行时数据区域负责的。class 对象的存在和操作是 JVM 执行 Java 程序的关键部分,它使得类的加载、验证、链接和运行成为可能。 Java 语言的多态性(通过 class 对象,Java 可以在运行时确定对象的实际类型,并根据实际类型调用适当的方法)和反射机制都是建立在 class 对象之上的。
在 Java 中,类的唯一性是由类加载器和类的全限定名共同确定的。不同的类加载器可以加载同名的类,但它们会被认为是不同的类,因为它们位于不同的命名空间中。这是 Java 中支持类加载器隔离和加载不同版本的类的机制之一。