• Java面向对象编程


    第一章 类与对象

    面向对象简介

    面向过程指的是面对于一个问题的解决方案,更多情况下不会做出重用的设计。

    面向对象主要设计形式为模块化设计,可以进行重用配置,更多情p况下考虑的是标准,然后根据标准进行拼装。

    面向对象有三个主要特性:

            封装性:内部的操作对外部不可见,当内部的操作都不可直接使用的时候才是安全的;

            继承性:在已有结构的基础上继续进行功能的扩充;

            多态性:在继承性的基础上扩充而来的概念,指的是类型的转换处理。

    面向对象程序开发的步骤:

            OOA:面向对象分析;OOD:面向对象设计;OOP:面向对象编程。

    类与对象简介

    类是对某一类事物共性的抽象概念。

    对象描述的是一个具体的产物。

    类是一个模板,而对象是类的实例,所以先有类才有对象。(类比为船的图纸(类)与船(对象),根据图纸可以造很多船)

    类的组成:

            成员属性(Field):简化称为”属性“,其对象的特征(人的姓名、人的年龄...),属性是指描述对象的一组数据,表现为对象的一些变量。

            操作方法(Method):表示对象的行为或所作的工作。

    类与对象的定义及使用

    Java中类是一个独立的结构体,所以需要用class来定义

    1. public class Student {
    2. // 类的属性:成员变量
    3. String name;
    4. int age;
    5. String like;
    6. // 成员方法(实例方法)
    7. public void show(){
    8. System.out.println("我叫"+name+",今年"+age+",我的爱好"+like);
    9. }
    10. // main方法
    11. public static void main(String[] args) {
    12. Student stu = new Student(); //创建对象,通过类来创建对象 new 类名()
    13. stu.name = "张三"; // 调用成员变量赋值:对象名.属性名
    14. stu.age = 20;
    15. stu.like = "干饭";
    16. stu.show(); // 调用成员方法
    17. }
    18. }

    语法:

            产生对象:

            一步完成: 声明并实例化对象:类名称 对象名称 = new 类名称();

            分步完成: 声明对象:类名称 对象名称 = null;

                               实例化对象:对象名称 = new 类名称();

            调用: 类中的属性:对象名称.成员属性

                        类中的方法:对象名称.方法名称()

    如果程序没有进行对象属性内容的设置,则数据内容为其对应数据类型的默认值。

    对象内存分析

    Java中类属于引用数据类型,困难之处在于要进行内存的管理。所以对内存管理进行简单分析。

    最常用的内存空间:

    堆内存:保存对象的具体信息,在程序中堆内存空间的开辟是通过new完成的;

    栈内存:保存单块堆内存的地址。即:通过通过地址找到堆内存,而后找到对象内容。但是为了分析简化起见,可以简单的理解为:对象名称保存在了栈内存之中。

    直接声明并实例化对象的内存分析:

    1. class Person { // 定义一个类
    2. String name; // 人的姓名
    3. int age; // 人的年龄
    4. public void tell() {
    5. System.out.println("姓名:" + name + ",年龄:" + age);
    6. }
    7. }
    8. public class JavaDemo {
    9. public static void main(String[] args) {
    10. Person per = new Person(); // 声明并实例化对象
    11. per.name = "张三";
    12. per.age = 18;
    13. per.tell(); // 进行方法的调用
    14. }
    15. }  

     分步骤声明再实例化对象的内存分析

    1. public class JavaDemo {
    2. public static void main(String[] args) {
    3. Person per = null; // 声明对象
    4. per = new Person(); // 实例化对象
    5. per.name = "张三";
    6. per.age = 18;
    7. per.tell(); // 进行方法的调用
    8. }
    9. }

    所有调用类的属性和方法在实例化对象完成后才能执行,调用只声明但没有实例化的对象

    1. public class JavaDemo {
    2. public static void main(String[] args) {
    3. Person per = null; // 声明对象
    4. per.name = "张三";
    5. per.age = 18;
    6. per.tell(); // 进行方法的调用
    7. }
    8. }

    上述代码会产生“java.lang.NullPointerException”的空指向异常,因为此时并没有在堆内存中开辟空间,这种情况只发生于引用数据类型。

    对象引用分析

    由于类属于引用数据类型,因此就牵扯到内存的引用传递。

    引用传递:指同一块堆内存被不同的栈内存所指向。

    在主方法中使用引用传递:

    1. public class JavaDemo {
    2. public static void main(String[] args) {
    3. Person per1 = new Person(); // 声明并实例化对象
    4. per1.name = "张三";
    5. per1.age = 18;
    6. Person per2 = per1; // 引用传递
    7. per2.age = 80;
    8. per1.tell(); // 进行方法的调用
    9. }
    10. }

    利用方法实现引用传递:

    与之前的差别最大的地方在于,此时的程序是将Person类的实例化对象(内存地址、属性的参数)传递到了change()方法中,由于传递的是一个Person对象类型,那么change()接收的也是Person类型。  

    1. public class JavaDemo {
    2. public static void main(String[] args) {
    3. Person per = new Person(); // 声明并实例化对象
    4. per.name = "张三";
    5. per.age = 18;
    6. change(per); // 等价于:Person temp = per;
    7. per.tell(); // 进行方法的调用
    8. }
    9. public static void change(Person temp) {
    10. temp.age = 80;
    11. }
    12. }

     引用传递可以发生在方法上,要观察方法里面传的的参数类型,同时也要观察方法的执行过程。

    引用与垃圾产生分析

    引用传递的本质:一场堆内存的调戏游戏。但引用传递,引用不当会产生垃圾。对垃圾产生原因进行简单分析。

    1. public class JavaDemo {
    2. public static void main(String[] args) {
    3. Person per1 = new Person(); // 声明并实例化对象
    4. Person per2 = new Person();
    5. per1.name = "张三";
    6. per1.age = 18;
    7. per2.name = "李四";
    8. per2.age = 19;
    9. per2 = per1; // 引用传递
    10. per2.age = 80;
    11. per1.tell(); // 进行方法的调用
    12. }
    13. }

     垃圾空间:没有任何栈内存所指向的堆内存空间,所有的垃圾都将被GC(Garbage Collector:垃圾收集器)不定期进行回收,并且释放无用空间,但如果垃圾过多,一定将影响到GC的处理性能,从而降低整体的程序性能。

    一个栈内存只能够保存一个堆内存的地址数据,如果发生更改,则之前的地址数据将从此栈内存中彻底消失。

    第二章 深入分析类与对象

    成员属性封装

    类的组成就是属性与方法,一般而言方法都是对外提供服务的,所以是不会进行封装处理的;而属性需要较高的安全性,所以就需要封装性对属性进行保护。

    对属性进行封装:使用private关键字。

    1. class Person { // 定义一个类
    2. private String name; // 人的姓名
    3. private int age; // 人的年龄
    4. public void tell() {
    5. System.out.println("姓名:" + name + ",年龄:" + age);
    6. }
    7. }

    属性一旦封装后,外部将不可以直接进行访问。

    【setter、getter】设置或取得属性使用setXxx()、getXxx()方法。

    设置属性方法:public void setName(String name) {};

    获取属性方法:public String getName() {};

    1. class Person { // 定义一个类
    2. private String name; // 人的姓名
    3. private int age; // 人的年龄
    4. public void tell() {
    5. System.out.println("姓名:" + name + ",年龄:" + age);
    6. }
    7. public String getName() {
    8. return name;
    9. }
    10. public void setName(String newName) {
    11. name = newName;
    12. }
    13. public int getAge() {
    14. return age;
    15. }
    16. public void setAge(int newAge) {
    17. age = newAge;
    18. }
    19. }
    20. public class JavaDemo {
    21. public static void main(String[] args) {
    22. Person per = new Person(); // 声明并实例化对象
    23. per.setName("张三"); // 在类外部通过方法修改私有属性
    24. per.setAge(18);
    25. per.tell(); // 进行方法的调用
    26. }
    27. }

    在定义类时,属性都应该使用private封装(98%),并提供setter、getter方法。

    构造方法与匿名对象

    现在的程序在使用类的时候一般都按照了如下的步骤进行:

    声明并实例化对象,这个时候实例化对象中的属性并没有任何的的数据存在,都是其对应数据类型的默认值。

    需要通过一系列的setter()方法设置属性值

    1. public class JavaDemo{ // 主类
    2. public static void main(String args[]){
    3. // 1、对象初始化准备
    4. Person per = new Person(); // 声明并实例化对象
    5. ​ per.setName("张三");
    6. ​ pei.setAge(-18); // 在类外部修改属性
    7. // 2、对象的使用
    8. ​ per.tell(); // 进行方法的调用
    9. ​ }
    10. }

    假设现在类中的属性有很多个,按上述方法,需要调用多次setter方法进行设置,过于麻烦,所有提供构造方法,实现实例化对象中的属性初始化处理。

    构造方法作用:实现实例化对象中的属性初始化处理。 使用:通过new关键字进行实现(因为实例化对象就是通过new关键字实现的)。

    定义要求:

            构造方法名称必须与类名称一致;

            构造方法不允许设置返回值类型,即:没有返回值定义;

            构造方法时在使用new关键字实例化对象时自动调用的。         

    1. class Person { // 定义一个类
    2. private String name; // 人的姓名
    3. private int age; // 人的年龄
    4. public Person(String n, int a) { // 定义有参构造
    5. name = n; // 为类中的属性赋值(初始化)
    6. age = a; // 为类中的属性赋值(初始化)
    7. }
    8. public void tell() {
    9. System.out.println("姓名:" + name + ",年龄:" + age);
    10. }
    11. }
    12. public class JavaDemo { // 主类
    13. public static void main(String[] args) {
    14. // 1、对象初始化准备
    15. Person per = new Person("张三", 18); // 声明并实例化对象
    16. // 2、对象的使用
    17. per.tell(); // 进行方法的调用
    18. }
    19. }

    实例化对象例子对比:

            之前:①Person ②per= ③new ④Person()

            当前:①Person ②per= ③new ④Person(“张三”, 18)

    解析:

            ①Person:定义对象的所属类型,类型决定了可以调用的方法;

            ②per=:实例化对象的名称,所有的操作通过对象来进行访问;

            ③new:开辟一块新的堆内存空间;

            ④Person():调用无参构造; ④Person(“张三”, 18):调用有参构造。

    在Java程序里面考虑到程序结构的完整性,所以所有的类都会提供有构造方法,如果你的类中没有定义的构造方法,那么程序会默认提供无参的构造方法,这个是在程序编译时自动创建的;

    如果你的类中有定义构造方法,那么程序则不会自动提供无参的构造方法。所以一个类至少存在有一个构造方法,且永恒存在。

    疑问:为什么构造方法上不允许设置返回值类型?

    程序编译器是根据代码结构来进行编译处理的,执行时也是根据代码结构来处理的。

    因此如果设置了返回值类型,那么构造方法的结构与普通方法的结构完全相同,这样编译器会认为此方法时一个普通方法。

    普通方法与构造方法最大的区别:构造方法是在类对象实例化时调用的,而普通方法是在类对象实例化产生之后才可以调用的。

    构造方法本身就是一个方法,其具有重载的特点,而构造方法重载的时候只需要考虑参数的类型及个数即可。

    进行多个构造方法定义的时候可以有定义的顺序,如按照参数的个数降序或升序进行。

    构造方法可以进行数据的设置,而setter也可以进行数据的设置。构造方法是在对象实例化的时候为属性设置初始化内容,而setter还拥有修改数据的内容。

    匿名对象:直接实例化对象来进行类的操作(没有声明对象,所以对象是没有名字的)。

    1. public class JavaDemo { // 主类
    2. public static void main(String[] args) {
    3. new Person("张三", 10).tell(); // 通过匿名对象直接进行方法的调用
    4. }
    5. }

    通过匿名对象调用方法,由于此对象没有任何的引用名称,因此该对象在使用一次之后就成为了垃圾,而所有的垃圾都将被GC进行回收与释放。

    通过以下程序,进行简短的内存分析。

    1. class Message{
    2. private String title;
    3. public Message(String t) {
    4. title = t;
    5. }
    6. public String getTitle() {
    7. return title;
    8. }
    9. public void setTitle(String t) {
    10. title = t;
    11. }
    12. }
    13. class Person{
    14. private String name;
    15. private int age;
    16. public Person(Message msg,int a ) {
    17. name = msg.getTitle();
    18. age = a;
    19. }
    20. public Message getInfo(){
    21. return new Message(name + ":" + age)
    22. }
    23. public void tell() {
    24. System.out.println("姓名" + name + "年龄" + age);
    25. }
    26. }
    27. public class No1PersonMessage {
    28. public static void main(String args[]) {
    29. Message msg = new Message("mldn");
    30. Person per = new Person(msg,20);
    31. msg = per.getInfo();
    32. System.out.println(msg.getTitle());
    33. }
    34. }

     只要是方法都可以传递任意的数据类型(基本数据类型、引用数据类型)。

    this关键字

    this可以实现以下三类 结构的描述:

            当前类中的属性:this.属性名;

            当前类中的方法:构造方法:this();普通方法:this.方法名();

            描述当前对象;

    在Java程序中,“{}”是作为一个结构体的边界符,所以在程序里面当进行变量(参数 / 属性)使用时,都会以“{}”作为查找边界。按照就近取用的原则,此时的构造方法并没有能够访问类中的属性,所以为了明确标记类中的属性和参数的区别,往往会在属性前追加一个this,表示本类的属性。

    只要是访问本类中的属性的时候,都要加this。

    1. class Person {
    2. private String name;
    3. private int age;
    4. public Person(String name, int age) {
    5. this.name = name;
    6. this.age = age
    7. }
    8. public void tell() {
    9. System.out.println("姓名:" + this.name + ",年龄:" + this.age);
    10. }
    11. }

    待续。。。

  • 相关阅读:
    使用弹性盒子flex对html进行布局和动态计算视口高度
    年终固定资产盘点如何快速准确?
    深入理解传输层协议:TCP与UDP的比较与应用
    1.6 CAN通信 F28335-Simulink仿真自动代码生成
    进程的调度算法
    [AI-ML]机器学习是什么?一起了解!(一)
    UE5 C++报错:is not currently enabled for Live Coding
    HCIP路由交换的三门考试
    SpringBoot War打包部署
    【原创】浅谈指针(十一)alloca函数
  • 原文地址:https://blog.csdn.net/weixin_44227733/article/details/118583431