Java面向对象思想以及原理以及内存图解

这篇具有很好参考价值的文章主要介绍了Java面向对象思想以及原理以及内存图解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是面向对象

面向对象和面向过程区别

面向过程:面向过程是将解决问题的思路转为一个个方法。
面向对象:面向对象则是编写一个对象,将这些思路封装成一个个对象方法,后续调用这个对象解决问题,相对面向过程而言,这种思路更符合人的思维并且更易扩展、复用、维护。

面向对象和面向过程性能差距:人们常常认为面向过程性能高于面向对象,因为创建的对象开销远远大于面向过程,实际上Java面向对象性能差的原因并不是这个,真正的原因是Java为半编译语言,运行并不是直接拿着二进制机械码执行,而是需要结果字节码转换这一步。
而且面向过程的性能并不一定比面向过程快,面向过程也需要计算偏移量以及某些脚本语言的性能也一定比Java好。

创建一个对象用什么运算符?

用new运算符,创建的对象的实例会在堆内存中开辟一个空间。而引用则在栈内存中,指向对象实例。

面向对象实现伪代码

以人开门为例,人需要开门,所以我们需要创建一个门对象,描述门的特征,这个门可以开或者关。所以门的伪代码如下:

门{
	开()
	{
	操作门轴
	}
}

上文说到了,面向对象的特点就是符合人的思维,而人开门这个功能,我们就可以创建一个人的对象,编写一个开门的动作,把门打开。通过这种对象调用对象的方式完成了功能。后续我们需要狗开门,猫开门也只是编写一个方法调用门对象的开的动作。

{
	开门(门对象){.()
	}
 }


面向对象三大特征

  1. 封装
  2. 继承
  3. 多态

类和对象的关系。

以生活事务为例,现实生活中的对象:张三 李四。他们都有姓名、性别、学习Java的能力。

所以我们要想通过面向对象思想实现抽象出对象,就得提取共性,编写一个类有姓名、性别、学习Java的能力。

public class Student {
    
    private String name;
    private int sex;
    
    public void studyJava(){
        System.out.println(this.name+"学习java");
    }
}

描述时,这些对象的共性有:姓名,年龄,性别,学习java功能。再将这些分析映射到java中,就是以class定义的类进行展开。

public static void main(String[] args) {
        Student zhangsan=new Student();
        zhangsan.setName("张三");
        zhangsan.studyJava();

        Student lisi=new Student();
        lisi.setName("李四");
        lisi.studyJava();
        
//        输出结果
//        张三学习java
//                李四学习java
    }

而具体对象就是对应java在堆内存中用new建立实体。

基础案例

需求:描述汽车(颜色,轮胎数)。描述事物其实就是在描述事物的属性和行为。

属性对应在类中即变量行为对应的类中的函数(方法)

代码实现

public class Car {
    //描述颜色
    String color = "红色";
    //描述轮胎数
    int num = 4;

    //运行行为。
    public void run() {

        System.out.println("颜色:"+color + " 轮胎数:" + num);
    }
}

实例化

public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.run();
    }
}

创建car对象时car引用的内存图

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

对象调用方法过程

首先我们看一段代码,这是一个人类的class类代码

public class Person {
    private String name;
    private int age;
    private static String country = "cn";

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void showCountry() {
        System.out.println("showCountry " + country);
    }


    public void speak() {
        System.out.println(this.getName() + " speak");
    }
}

假如我们在main中编写这样一段代码,请问在内存中他是如何工作的呢?

 public static void main(String[] args) {
        Person p = new Person("张三", 18);
        p.setName("李四");
    }

我们先从类类加载时开始分析,由于static关键字修改的变量或者方法会随着jvm加载类时一起创建,所以countryshowCountry()在方法区是这样的。

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

然后main方法开始执行对应代码,首先main方法入栈,初始化一个p引用

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

堆区开辟一个空间,创建一个person实例,p引用指向这个内存空间

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

调用setName,setName入栈,完成name值修改之后销毁

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

成员变量和局部变量

作用范围

成员变量作用于整个类中。
局部变量变量作用于函数中,或者语句中。

在内存中的位置

成员变量:在堆内存中,因为对象的存在,才在内存中存在。
局部变量:存在栈内存中。

关于对象的引用关系

简介

对象引用用于指向0个或者多个对象实例,对象实例可以被多个对象引用指向。

相关代码

假如我们使用上文car类执行以下代码,那么在内存中会如何执行呢?

car c=new car();
c.num=5;
car c1=c;
c.run();

内存图解

  1. 首先堆区开辟一个空间创建car对象,初始化值
  2. 修改num为5
  3. c1引用指向c,如下图所示
    匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

对象相等和引用相等的区别

  1. 对象相等:两个对象内存中的存放的内容都相等
  2. 引用相等:两个引用指向的内存地址相等。

类的构造方法的作用是什么

完成对象初始化,首先在堆区创建对象实例。

构造方法的特点

  1. 与类名相同
  2. 无返回值
  3. 生成对象时自动执行
  4. 不可重写可以重载

深拷贝和浅拷贝区别

浅拷贝

对象进行拷贝时,如果内部有引用类型,克隆对象仅仅是复制被克隆内部对象的引用地址

为了介绍浅拷贝我们贴出这样一段代码,可以看到一个学生类有id和name,以及一个Vector的引用对象

public class Student implements Cloneable {
    private String id;
    private String name;
    private Vector<String> vector;


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Vector<String> getVector() {
        return vector;
    }

    public void setVector(Vector<String> vector) {
        this.vector = vector;
    }


    public Student() {
        try {
            System.out.println("创建对象需要三秒......");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public Student newInstance() {
        try {
            return (Student) this.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return null;
    }

  
}

然后我们使用下面这段代码进行测试,可以看到输出结果为true,说明student2的vector是student1的。如下图所示,克隆对象的内部引用对象和student1是通用的

@Test
    public void cloneTest() throws CloneNotSupportedException {

        long start,end;
        start=System.currentTimeMillis();
        Student student=new Student();
        end=System.currentTimeMillis();
        System.out.println("学生1创建时间长 "+(end-start));


        student.setId("1");
        student.setName("小明");
        Vector<String> v = new Vector<>();
        v.add("000000");
        v.add("000001");
        student.setVector(v);

        start=System.currentTimeMillis();
        Student student2= student.newInstance();
        end=System.currentTimeMillis();
        System.out.println("学生2创建时间长 "+(end-start));


        for (String s : student2.getVector()) {
            System.out.println(s);
        }
//        false则说明深拷贝成功
        System.out.println(student.getVector()==student2.getVector());
    }

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

深拷贝

了解了浅拷贝之后,我们就可以解释深拷贝了,克隆对象的内部引用对象都是全新复制出来的一份

基于上文student代码我们对此进行改造,重写以下clone方法

@Override
    protected Object clone() throws CloneNotSupportedException {
        Student clone = new Student();
        clone.setId(this.getId());
        clone.setName(this.getName());

        //避免clone导致浅拷贝问题
        Vector<String> srcVector = this.getVector();

        Vector<String> dstVector = new Vector<>();
        for (String v : srcVector) {
            dstVector.add(v);
        }

        clone.setVector(dstVector);

        return clone;

    }

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

匿名对象

实例代码

如下所示,在堆区创建一个对象实例,用后即被销毁。为了介绍匿名对象,我们首先需要编写一个汽车类

public class Car {
    //描述颜色
    private String color = "红色";
    //描述轮胎数
    private int num = 4;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    //运行行为。
    public void run() {
        this.setNum(++num);
        System.out.println("颜色:" + color + " 轮胎数:" + num);
    }
}

然后我们使用测试单元进行测试


@Test
    public void anonymously(){
        new Car().run();

    }
		

上述代码的工作过程如下所示,可以看到完成方法调用之后

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

匿名对象与实例对象的区别

实例代码

可以看到我们现实创建一个非匿名的汽车类和匿名汽车类,并作为show方法的参数传入


public static void main(String[] args) {
        Car car = new Car();
        show(car);
        show(new Car());
        /**
         * 输出结果
         * 颜色:black 轮胎数:4
         * 颜色:black 轮胎数:4
         */
    }


    public static void show(Car c) {
        c.setNum(3);
        c.setColor("black");
        c.run();
    }

图解匿名与非匿名内存运行

非匿名对象内存运行过程图解

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

匿名对象完成方法调用后即被销毁

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

使用场景

  1. 当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。如果对一个对象进行多个成员调用,必须给这个对象起个名字。

  2. 可以将匿名对象作为实际参数进行传递。

封装

什么是封装

以生活为例子,某公司老板招开发人员,招得开发人员后,开发人员工作过程不用看到,老板只关注开发结果,而老板只看到开发结果这一现象即封装。

什么时private修饰

private :私有,权限修饰符:用于修饰类中的成员(成员变量,成员函数)。私有只在本类中有效。

代码示例

如下所示,setAge就是对age赋值的封装,隐藏对年龄操作的细节,用户只需通过这个方法完成自己需要的赋值动作即可

public class Person {
    private int age;

    public void setAge(int a) {
        if (a > 0 && a < 130) {
            age = a;
            speak();
        } else
            System.out.println("feifa age");
    }

    public int getAge() {
        return age;
    }

    private void speak() {
        System.out.println("age=" + age);
    }
}

构造函数

什么是构造函数

  1. 对象一建立就会调用与之对应的构造函数。
  2. 构造函数的作用:可以用于给对象进行初始化。

构造函数的小细节

  1. 类默认有构造函数,显示创建后默认构造类就消失。
  2. 默认构造函数权限和类权限修饰符一致,例如类权限为public,则构造方法默认也为public,除非显示修改权限。

构造代码块

构造代码块示例以及与构造方法的区别

构造代码块。

作用:给对象进行初始化。
对象一建立就运行,而且优先于构造函数执行。
和构造函数的区别:
1. 构造代码块是给所有对象进行统一初始化,在jvm完成类加载时就会运行方法,也就是说调用静态方法的情况下,构造代码块也会被执行
2. 而构造函数是给对应的对象初始化。

构造代码块会随着对象实例的创建和运行。

public class Person {
    private String name;
    private int age;


    {
        System.out.println("person 类的构造代码块执行了");
        run();
    }
    public void run(){
        System.out.println("person run");
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}

如下便是main函数的运行结果

public static void main(String[] args) {
        Person p=new Person();
        /**
         * person 类的构造代码块执行了
         * person run
         */
    }

this关键字

什么是this关键字

代表它所在函数所属对象的引用。简单来说,调用对象方法的对象就是this关键字多代表的对象。

this的应用

解决构造函数初始化的问题

如下代码,假如所有成员变量不加this,编译器则不会找成员变量name,导致赋值过程毫无意义。

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言
输出结果

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

对此我们就可以使用this关键字即可解决问题

public class Person {
    private String name;
    private int age;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

用于构造函数之间进行互相调用

注意:this语句只能定义在构造函数的第一行。

public Person(String name, int age) {
        this(name);
        this.age = age;
    }

    public Person(String name) {
        this.name = name;
    }

static关键字

什么是static关键字

用法:是一个修饰符,用于修饰成员(成员变量,成员函数).
当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用。类名.静态成员。

static特点

  1. 随着类的加载而加载。随着jvm完成类加载该变量或者方法就会被加载到方法区。
  2. 静态会随着类的消失而消失。说明它的生命周期最长。
  3. 优先于对象存在,即静态变量先存在,对象后存在。
  4. 被所有对象所共享,所以有时候我们需要考虑线程安全问题。
  5. 可以直接被类名所调用。

实例变量和类变量的区别

  1. 实例变量随着对象的创建而存放在堆内存上,而类变量即静态变量随着类加载而存放在方法区上。
  2. 实例变量随着对象实例消亡而消亡,而类变量随着类的消亡和消亡。

静态使用注意事项:

  1. 静态方法只能访问静态变量,实例对象则静态非静态都可以访问
  2. 静态方法不可使用this和super关键字,因为this和super都是对象实例的关键字,this关键字是指向对象实例,static关键字在类加载时候就能被指向,故不可使用这两个关键字。

静态有利有弊

利处

随着类加载而创建,每个对象公用一份,无需每个实例到堆区创建一份。

弊处

  1. 生命周期长
  2. 使用不当可能造成线程安全问题。
  3. 访问有局限性,静态方法只能访问静态相关变量或者方法。

错误代码示范

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

图解对象如何调用static变量

我们首先编写这样一段代码

public class Person {
    private String name;
    private int age;

    public static int staticVar=4;
    
   

}

然后我们的main方法进行这样的调用,在jvm内存是如何执行的呢?

 public static void main(String[] args) {
       Person person=new Person();
       person.staticVar=5;
               
    }
  1. main方法入栈
  2. 堆区创建person对象实例,p指向实例
  3. p实例通过堆区对象操作方法的静态变量,修改值为5

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

main函数

主函数

主函数是一个特殊的函数,程序执行的入口,可以被jvm执行。

主函数的定义格式以及关键字含义

public:代表着该函数访问权限是最大的。
static:代表主函数随着类的加载就已经存在了。
void:主函数没有具体的返回值。
main:不是关键字,但是是一个特殊的单词,可以被jvm识别。
(String[] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组。

主函数是固定格式的

只有符合上述的固定格式,jvm才能识别。

jvm在调用主函数时,传入的是new String[0];即可长度为0的String

如何使用args

class MainDemo 
{
	public static void main(String[] args)//new String[]
	{
		String[] arr = {"hah","hhe","heihei","xixi","hiahia"};

		MainTest.main(arr);
	}
}


class MainTest
{
	public static void main(String[] args)
	{
		for(int x=0; x<args.length; x++)
			System.out.println(args[x]);
	}
}

静态代码块

格式

static
{
	静态代码块中的执行语句。
}

我们在person类中编写一个静态代码块,然后调用其他静态方法,可以发现静态代码块会随着类的加载而完成执行,并且只执行一次

public class Person {
    

    static {
        System.out.println("静态代码块,随着方法执行而执行.....");
    }

    public static void func(){
        System.out.println("静态方法执行了");
    }
    
}

main方法调用示例

 public static void main(String[] args) {
       Person.func();
       Person.func();
        /**
         * 输出结果
         * 静态代码块,随着方法执行而执行.....
         * 静态方法执行了
         * 静态方法执行了
         */

    }

设计优化

单例模式

简介

对于重量级对象的创建可能会导致以下问题:

  1. 创建对象开销大
  2. GC压力大,可能导致系统卡顿

饿汉式

代码如下所示,可以看到对象随着类的加载就会立刻完成创建,这就导致假如我们使用这个类的某些不需要单例的方法也会完成对象的创建。
例如我们就像调用以下Singleton 的sayHello这个静态方法就会导致单例实例被创建,所以如果非必要我们不建议采用这种非延迟加载的单例模式

public class Singleton {
    private Singleton() {
        System.out.println("创建单例");
    }

    public static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }

    public static void sayHello(){
        System.out.println("hello");
    }

    
}

测试代码,可以看到调用静态方法单例就被被创建了

public static void main(String[] args) {

        Singleton.sayHello();
        /**
         * 输出:
         * 创建单例
         * hello
         */

    }

原理也很简单,静态变量和方法都在方法区,随着类被加载这些变量或者方法都会被加载。

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

懒汉式(线程不安全)

懒汉式即实现延迟加载的有效手段,代码如下所示

/**
 * 延迟加载的单例类 避免jvm加载时创建对象
 */
public class LazySingleton {
    private LazySingleton() {
        System.out.println("懒加载单例类");
    }

    private static LazySingleton instance = null;

    
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

    public static void sayHello(){
        System.out.println("hello");
    }

   
}

调用示例如下所示,可以看到静态方法调用后并没有创建实例,只有调用获取对象时才会得到对象实例

public static void main(String[] args) {
        LazySingleton.sayHello();
        LazySingleton.getInstance();
        /**
         * 输出结果
         * hello
         * 懒加载单例类
         */
    }

懒汉式的工作原理如下图所示,可以看到只有调用getInstance后,才会在堆内存中开辟一块内存空间创建对象

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

实际上,当前的懒汉式存在线程安全问题,如上内存图解所示,可能会有两个线程走到==null的判断中进而出现创建多个单例对象的情况。我们使用JUC的倒计时门闩调用获取单例的情况,可以看到对象被创建了多次。

 /**
     * 不加synchronized的懒加载 加上则没有下面这样输出结果
     */
    @Test
    public void threadTest0(){
        ExecutorService threadPool = Executors.newFixedThreadPool(1000);
        CountDownLatch countDownLatch=new CountDownLatch(1);
        for (int i = 0; i < 10000; i++) {
            threadPool.submit(()->{
                System.out.println(LazySingleton.getInstance());
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }


        countDownLatch.countDown();
        /**
         * 懒加载单例类
         * 懒加载单例类
         * 懒加载单例类
         * 懒加载单例类
         * com.optimize.design.LazySingleton@12423874
         * com.optimize.design.LazySingleton@350c55ec
         * com.optimize.design.LazySingleton@350c55ec
         * com.optimize.design.LazySingleton@350c55ec
         * 懒加载单例类
         * com.optimize.design.LazySingleton@350c55ec
         * com.optimize.design.LazySingleton@5897fc07
         * 懒加载单例类
         * com.optimize.design.LazySingleton@5897fc07
         * 懒加载单例类
         * com.optimize.design.LazySingleton@39d8305
         * com.optimize.design.LazySingleton@1a0eae7f
         * com.optimize.design.LazySingleton@1a0eae7f
         * 懒加载单例类
         * com.optimize.design.LazySingleton@1a0eae7f
         * 懒加载单例类
         */
    }

懒汉式(线程安全)

要想实现线程安全,我们只需要通过下面这种方式上锁即可保线程安全,但是缺点也很明显,在高并发情况下,获取对象的实践会随着增加

 /**
     * 增加 synchronized确保线程安全
     * @return
     */
    public synchronized static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

测试用例如下,可以看到饿汉式和线程安全懒汉式时间的差距

@Test
    public void test(){
        long start=System.currentTimeMillis();
        for (int i = 0; i < 100_0000; i++) {
            Singleton.getInstance();
        }
        long end=System.currentTimeMillis();
        System.out.println(end-start);


         start=System.currentTimeMillis();
        for (int i = 0; i < 100_0000; i++) {
            LazySingleton.getInstance();
        }
         end=System.currentTimeMillis();
        System.out.println(end-start);





        /**
         * 输出结果
         *
         * 创建单例
         * 3
         * 懒加载单例类
         * 20
         */
    }

内部类模式

上文提到的懒汉式的性能问题,所以我们可以使用内部类模式解决该问题,代码如下所示,可以看到我们在单例类的内部增加一个静态内部类,该类被加载时静态内部类并不会被加载,只有调用getInstance才会创建单例对象,并且该对象的创建是随着类的加载就完成创建,故这是一种线程友好的单例模式

/**
 * 线程安全的单例 但还是会被反射攻破
 */
public class StaticSingleton {
    private StaticSingleton() {
        System.out.println("静态内部类延迟加载");
    }

    private static class SingletonHolder {
        private static StaticSingleton instance = new StaticSingleton();
    }

    public static StaticSingleton getInstance(){
        return SingletonHolder.instance;
    }

    public static void sayHello(){
        System.out.println("hello");
    }

    
}

测试代码和输出结果

public static void main(String[] args) {
        StaticSingleton.sayHello();
        StaticSingleton.getInstance();
        /**
         * 输出结果
         * 
         * hello
         * 静态内部类延迟加载
         */
    }

内部类单例模式工作过程

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

实际上这种模式也有缺点,就是会被发射攻破,后续我们会介绍对应的解决方案

双重锁校验(线程安全)

双重锁校验的单例模式如下所示,可以看到双重锁校验的编码方式和简单,第一次判断避免没必要的执行,第二次判断避免第一次判定为空走到创建对象代码块的线程,从而避免线程安全问题

public class DoubleCheckLockSingleton {

    private static DoubleCheckLockSingleton instance = null;

    private DoubleCheckLockSingleton() {
        System.out.println("双重锁单例对象被创建");
    }

    public static DoubleCheckLockSingleton getInstance() {
        if (instance != null) {
            return instance;
        }

        synchronized (DoubleCheckLockSingleton.class) {
            //这一重校验是为了避免上面判空后进入休眠走到这个代码块的线程
            if (null == instance) {
                instance = new DoubleCheckLockSingleton();
                return instance;
            }
        }
        return instance;
    }


}

性能上我们可以看到双重锁校验的性能要好于静态内部类的方式

 @Test
    public void test(){
        long start=System.currentTimeMillis();
        for (int i = 0; i < 100_0000; i++) {
            Singleton.getInstance();
        }
        long end=System.currentTimeMillis();
        System.out.println(end-start);


         start=System.currentTimeMillis();
        for (int i = 0; i < 100_0000; i++) {
            LazySingleton.getInstance();
        }
         end=System.currentTimeMillis();
        System.out.println(end-start);


        start=System.currentTimeMillis();
        for (int i = 0; i < 100_0000; i++) {
            StaticSingleton.getInstance();
        }
        end=System.currentTimeMillis();
        System.out.println(end-start);


        start=System.currentTimeMillis();
        for (int i = 0; i < 100_0000; i++) {
            DoubleCheckLockSingleton.getInstance();
        }
        end=System.currentTimeMillis();
        System.out.println(end-start);


        /**
         * 创建单例
         * 6
         * 懒加载单例类
         * 20
         * 静态内部类延迟加载
         * 4
         * 双重锁单例对象被创建
         * 3
         */
    }

枚举单例模式(线程安全)

/**
 * 使用枚举保证类单例
 */
public enum Elvis {
    INSTANCE;
    private String name="elvis";

    public String getName() {
        return name;
    }

    public static Elvis getInstance(){
        return INSTANCE;
    }


    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }


}

可以看到这种方式不会被反射攻破

public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class<Elvis> elvisClass = Elvis.class;
        Elvis elvis1 = elvisClass.newInstance();
        System.out.println(elvis1.getName()); 

    }

输出结果

匿名内存和非匿名内存,Java,java,封装,多态,类,编程语言

相关面试题

下面这段代码。new StaticCode(4)的输出结果?

public class StaticCode {
    int num = 9;

    StaticCode() {
        System.out.println("b");
    }

    static {
        System.out.println("a");
    }

    {
        System.out.println("c" + this.num);
    }

    StaticCode(int x) {
        System.out.println("d");
    }

    public static void show() {
        System.out.println("show run");
    }
}

答案:a c9 d

创建类,加载顺序为:

  1. 加载静态代码块
  2. 加载构造代码块
  3. 加载构造方法

参考文献

Java基础常见面试题总结(中)

Effective Java中文版(第3版)

Java系统性能优化实战文章来源地址https://www.toymoban.com/news/detail-757735.html

到了这里,关于Java面向对象思想以及原理以及内存图解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【JavaSE】面向对象编程思想之继承

     【本节目标】 1. 继承 2. 组合 目录 1. 为什么需要继承 2. 继承概念 3. 继承的语法 4. 父类成员访问 4.1 子类中访问父类的成员变量 4.2 子类中访问父类的成员方法 5. super 6. 子类构造方法 7. super和this 8. 再谈初始化 9. protected 10. 继承方式 11. final 12 继承与

    2024年02月12日
    浏览(40)
  • 【JavaSE】面向对象编程思想之多态(图文详解)

    目录 1. 多态的概念 2. 多态实现条件 3. 重写 4. 向上转型和向下转型 4.1 向上转型 4.2 向下转型 5. 多态的优缺点 6. 避免在构造方法中调用重写的方法 多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。  总的来说

    2024年02月14日
    浏览(42)
  • java面向对象——继承以及super关键字

    在 同⼀类 中,存在⽅法名相同,参数列表不同(个数、类型或顺序不同)的⽅法互为重载。 在 继承关系 中,⼦类声明⼀个继承⾃⽗类的⽅法名相同、参数列表相同,返回值类型⼀致,访问修饰符相同或 变⼤,抛出异常相同或缩⼩的⽅法称为重写。 重载是编译时多态,重写

    2024年02月12日
    浏览(39)
  • 数据可视化——结合面向对象的思想实现数据可视化

    前面我们已经学习了如何使用 python 的 pyecharts 模块来实现数据可视化,将数据经过处理后以折线图、地图以及柱状图的形式展现出来,那么这篇文章我将以一个例子为大家分享如何结合 面向对象 的思想来实现数据可视化。 收集数据:收集需要进行可视化的数据,并确保数据

    2024年02月16日
    浏览(46)
  • Java面向对象——多态、Object类、instanceof关键字以及final关键字

    总之,多态是面向对象编程中一个非常重要的概念,通过它可以实现统一的接口来操作不同的对象,提高代码的可读性和可维护性。在实际编程中,多态性的使用可以使代码更加灵活和扩展性更强。方法重写是实现多态的基础。 重写如下所示 关于hashCode方法的重写: 重写t

    2024年02月12日
    浏览(67)
  • 创造与布局:剖析 Java 对象创建过程以及内存布局

    目录 上下文提及到了类的加载过程,详细介绍了加载类的每个阶段:Loading、Linking、Initialize,在其中也说明了静态变量赋值顺序 先赋予默认值、在 Initialize 初始化阶段赋予初始值 从类加载到双亲委派:深入解析类加载机制与 ClassLoader 该篇文章会详细实例对象的创建过程、对

    2024年02月11日
    浏览(37)
  • 【IMX6ULL驱动开发学习】11.驱动设计之面向对象_分层思想(学习设备树过渡部分)

    一个 可移植性好 的驱动程序,应该有三个部分组成 1、驱动框架程序(xxx_drv.c) — 对接应用层的 open read write 函数,不做GPIO具体操作 2、硬件操作程序(xxx_chip_gpio.c)— 执行具体的GPIO操作,初始化、读写 3、硬件资源定义程序(xxx_board.c,这在之后就过渡成了设备树)— 为

    2024年02月11日
    浏览(42)
  • 面向对象的介绍和内存

    学习面向对象内容的三条主线 • Java 类及类的成员 :(重点)属性、方法、构造器;(熟悉)代码块、内部类 • 面向对象的特征 :封装、继承、多态、(抽象) • 其他的使用 :this、super、package、import、static、final、interface、 abstract 等 程序设计的思路 面向对象,

    2024年02月08日
    浏览(32)
  • C++面向对象丨1. 内存分区模型

    Author:AXYZdong 硕士在读 工科男 有一点思考,有一点想法,有一点理性! 定个小小目标,努力成为习惯!在最美的年华遇见更好的自己! CSDN@AXYZdong,CSDN首发,AXYZdong原创 唯一博客更新的地址为: 👉 AXYZdong的博客 👈 B站主页为: AXYZdong的个人主页 系列文章目录 C++基础入门

    2023年04月18日
    浏览(36)
  • java中this的内存原理以及成员变量和局部变量

    区分局部变量和成员变量 eg: 代表所在方法调用者的地址值 代表所在方法调用者的地址值: 此时main方法里面的调用者是s,s记录的地址值是001;this的本质是方法调用者的地址值,所以this指向的地址值是001。 this的内存原理: 解析: 等号的右边name出发了就近原则,表示setN

    2024年02月04日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包