JavaSE-面向对象思想、原理以及内存图解

    技术2022-07-15  72

    什么是面向对象

    思想

    即由面向过程的动作者变为动作的指挥者

    伪代码

    //以人开门为例{ 开门(门对象){.() } }{() { 操作门轴 } }

    特征

    封装继承多态

    类和对象的关系。

    以生活事务为例,现实生活中的对象:张三 李四。 想要描述这些人时:提取对象中共性内容,对具体的抽象。 描述时:这些对象的共性有:姓名,年龄,性别,学习java功能。再将这些分析映射到java中,就是以class定义的类进行展开。

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

    小结: 类:对现实生活中事物的描述。 对象:就是这类事物,实实在在存在个体。

    基础案例

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

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

    代码实现

    class Car { //描述颜色 String color = "红色"; //描述轮胎数 int num = 4; //运行行为。 void run() { System.out.println(color+".."+num); } }

    内存图

    对象调用方法过程

    下图执行步骤:

    main方法入栈,初始化p引用堆区开辟一个空间,创建一个person类,p指向0x0023的类setName方法入栈,将name值修改后销毁

    可以看到方法区的country和showconutry方法一直随着类的存在而存在,且所有对象共用。

    成员变量和局部变量

    作用范围

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

    在内存中的位置

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

    关于对象的引用关系

    相关代码

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

    内存图解

    匿名对象

    代码

    new Car().num=5; new Car().run();

    图解

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

    代码

    show(){ c.num=3; c.color="black" c.run(); } main(){ //实例对象 car c=new car(); show(c); //匿名对象 show(new car()); }

    图解

    使用场景

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

    匿名对象使用方式二:可以将匿名对象作为实际参数进行传递。

    封装

    什么是封装

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

    private修饰

    什么时private修饰

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

    代码示例

    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); } }

    set get内存图解

    构造函数

    什么是构造函数

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

    构造函数的小细节

    当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数。当在类中自定义了构造函数后,默认的构造函数就没有了。 一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致。如果类被public修饰,那么默认的构造函数也带public修饰符。如果类没有被public修饰,那么默认的构造函数,也没有public修饰。

    默认构造构造函数的权限是随着的类的变化而变化的。

    构造函数何时运行

    构造函数是在对象一建立就运行。给对象初始化。

    而一般方法是对象调用才执行,给是对象添加对象具备的功能。一个对象建立,构造函数只运行一次。而一般方法可以被该对象调用多次。

    什么时候定义构造函数呢?

    当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。

    构造代码块

    如下代码,假如所有对象都需要cry可以将其放置在构造代码块中。

    { private String name; private int age; /* 构造代码块。 作用:给对象进行初始化。 对象一建立就运行,而且优先于构造函数执行。 和构造函数的区别: 构造代码块是给所有对象进行统一初始化, 而构造函数是给对应的对象初始化。 构造代码快中定义的是不同对象共性的初始化内容。 */ { //System.out.println("person code run"); cry(); } Person() { System.out.println("A: name="+name+",,age="+age); } /**/ Person(String n) { name = n; System.out.println("B: name="+name+",,age="+age); //cry(); } Person(String n,int a) { name = n; age = a; System.out.println("C: name="+name+",,age="+age); } public void cry() { System.out.println("cry......"); } } class PersonDemo2 { public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person("lisi"); } }

    this关键字

    什么是this关键字

    this:就代表本类的对象,到底代表哪一个呢? this代表它所在函数****所属对象的引用。 简单说:哪个对象在调用this所在的函数,this就代表哪个对象。

    this的应用

    当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。 但凡本类功能内部使用了了本类对象,都要用this表示。

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

    如下代码,假如所有成员变量不加this,编译器则不会找成员变量name,而是直接用函数的name赋值给name,赋值过程毫无意义。

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

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

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

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

    需要注意的是下述对方会造成死循环,使用this关键字时需要谨慎

    Person() { this("hah"); System.out.println("person run"); } Person(String name) { this(); this.name =name; }

    static关键字

    什么是static关键字

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

    static特点

    1,随着类的加载而加载。 也就说:静态会随着类的消失而消失。说明它的生命周期最长。

    2,优先于的对象存在 明确一点:静态是先存在。对象是后存在的。

    3,被所有对象所共享 4,可以直接被类名所调用。

    实例变量和类变量的区别

    1,存放位置。 类变量(即static关键字修饰的变量)随着类的加载而存在于方法区中。 实例变量随着对象的建立而存在于堆内存中。 2,生命周期: 类变量生命周期最长,随着类的消失而消失。 实例变量生命周期随着对象的消失而消失。

    静态使用注意事项:

    静态方法只能访问静态成员。 非静态方法既可以访问静态也可以访问非静态。静态方法中不可以定义this,super关键字。 因为静态优先于对象存在。所以静态方法中不可以出现this。主函数是静态的。

    静态有利有弊

    利处:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。可以直接被类名调用。 弊端:生命周期过长。 访问出现局限性。(静态方法虽好,只能访问静态变量。)

    错误代码示范

    静态不可访问非静态

    错误: 无法从静态上下文中引用非静态 变量 this this.haha(); ^

    class Person { String name;//成员变量,实例变量。 static String country = "CN";//静态的成员变量,类变量。 public static void show() { System.out.println("::::"); this.haha(); } public void haha() {} }

    图解对象如何调用static变量

    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]); } }

    静态函数的使用

    需求分析

    每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装。以便复用。

    虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作。 发现了问题: 1,对象是用于封装数据的,可是ArrayTool对象并未封装特有数据。 2,操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。

    这时就考虑,让程序更严谨,是不需要对象的。 可以将ArrayTool中的方法都定义成static的。直接通过类名调用即可。

    将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。 为了更为严谨,强制让该类不能建立对象。 可以通过将构造函数私有化完成。

    代码示范

    public class ArrayTool { /** 空参数构造函数。 设置为private反正其他开发人员建立实例对象 */ private ArrayTool(){} /** 获取一个整形数组中的最大值。 @param arr 接收一个int类型的数组。 @return 会返回一个该数组中最大值。 */ public static int getMax(int[] arr) { int max = 0; for(int x=1; x<arr.length; x++) { if(arr[x]>arr[max]) max = x; } return arr[max]; } /** 获取一个整形数组中的最小值。 @param arr 接收一个int类型的数组。 @return 会返回一个该数组中最小值。 */ public static int getMin(int[] arr) { int min = 0; for(int x=1; x<arr.length; x++) { if(arr[x]<arr[min]) min = x; } return arr[min]; } /** 给int数组进行选择排序。 @param arr 接收一个int类型的数组。 */ public static void selectSort(int[] arr) { for (int x=0; x<arr.length-1 ; x++ ) { for(int y=x+1; y<arr.length; y++) { if(arr[x]>arr[y]) { swap(arr,x,y); } } } } /** 给int数组进行冒泡排序。 @param arr 接收一个int类型的数组。 */ public static void bubbleSort(int[] arr) { for (int x=0; x<arr.length-1 ; x++ ) { for(int y=0; y<arr.length-x-1; y++) { if(arr[y]>arr[y+1]) { swap(arr,y,y+1); } } } } /** 给数组中元素进行位置的置换。 @param arr 接收一个int类型的数组。 @param a 要置换的位置 @param b 要置换的位置 */ private static void swap(int[] arr,int a,int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } /** 用于打印数组中的元素。打印形式是:[elemet1, element2, ...] */ public static void printArray(int[] arr) { System.out.print("["); for(int x=0; x<arr.length; x++) { if(x!=arr.length-1) System.out.print(arr[x]+", "); else System.out.println(arr[x]+"]"); } } }

    一个开发的小细节

    当使用javac代码编译下方程序时,看到ArrayTool类,编译器就会在当前文件寻找,若有则先生成class,若没有则报错

    class ArrayToolDemo { public static void main(String[] args) { int[] arr = {3,1,87,32,8}; int max = ArrayTool.getMax(arr); System.out.println("max="+max); } }

    文档编写

    在团队项目开发中,说明文档也是很重要的,说明书通过文档注释来完成。

    代码示范

    /** 这是一个可以对数组进行操作的工具类,该类中提供了,获取最值,排序等功能。 @author 张三 @version V1.1 */ //javadoc -d myhelp -author -version ArrayTool.java public class ArrayTool { /** 空参数构造函数。 */ private ArrayTool(){} /** 获取一个整形数组中的最大值。 @param arr 接收一个int类型的数组。 @return 会返回一个该数组中最大值。 */ public static int getMax(int[] arr) { int max = 0; for(int x=1; x<arr.length; x++) { if(arr[x]>arr[max]) max = x; } return arr[max]; } /** 获取一个整形数组中的最小值。 @param arr 接收一个int类型的数组。 @return 会返回一个该数组中最小值。 */ public static int getMin(int[] arr) { int min = 0; for(int x=1; x<arr.length; x++) { if(arr[x]<arr[min]) min = x; } return arr[min]; } /** 给int数组进行选择排序。 @param arr 接收一个int类型的数组。 */ public static void selectSort(int[] arr) { for (int x=0; x<arr.length-1 ; x++ ) { for(int y=x+1; y<arr.length; y++) { if(arr[x]>arr[y]) { swap(arr,x,y); } } } } /** 给int数组进行冒泡排序。 @param arr 接收一个int类型的数组。 */ public static void bubbleSort(int[] arr) { for (int x=0; x<arr.length-1 ; x++ ) { for(int y=0; y<arr.length-x-1; y++) { if(arr[y]>arr[y+1]) { swap(arr,y,y+1); } } } } /** 给数组中元素进行位置的置换。 @param arr 接收一个int类型的数组。 @param a 要置换的位置 @param b 要置换的位置 */ private static void swap(int[] arr,int a,int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } /** 用于打印数组中的元素。打印形式是:[elemet1, element2, ...] */ public static void printArray(int[] arr) { System.out.print("["); for(int x=0; x<arr.length; x++) { if(x!=arr.length-1) System.out.print(arr[x]+", "); else System.out.println(arr[x]+"]"); } } }

    使用命令生成文档

    javadoc -d myhelp -author -version ArrayTool.java

    文档位置

    注意事项

    需要生成文档的类必须时以public修饰

    静态代码块

    格式

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

    特点

    随着类的加载而执行,只执行一次,并优先于主函数。 用于给类进行初始化的。

    面试题

    new StaticCode(4)的输出结果? 答案:先顺序执行 输出a 输出c9,再到构造函数,注意这里num必须加this,否则会报错。 a c9 d

    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"); } } class StaticCodeDemo { static { //System.out.println("b"); } public static void main(String[] args) { new StaticCode(4);//a c d } }

    单例设计模式

    设计模式概述

    设计模式:解决某一类问题最行之有效的方法。 java中23种设计模式: 单例设计模式:解决一个类在内存只存在一个对象。

    想要保证对象唯一。

    为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

    这三部怎么用代码体现呢?

    将构造函数私有化。在类中创建一个本类对象。提供一个方法可以获取到该对象。

    饿汉式

    类一加载就创建对象 ### 代码

    class Single { private static Single s = new Single(); private Single(){} public static Single getInstance() { return s; } }

    懒汉式

    //对象是方法被调用时,才初始化,也叫做对象的延时加载。成为:懒汉式。 //Single类进内存,对象还没有存在,只有调用了getInstance方法时,才建立对象。 class Single { private static Single s = null; private Single(){} public static Single getInstance() { if(s==null) { synchronized(Single.class) { if(s==null) s = new Single(); } } return s; } }

    图解

    Processed: 0.036, SQL: 9