Java基础—面向对象(一)

面向对象(一)

一、理解面向对象

  1. 面向对象作为一种编程思想,其本质是基于面向过程的。相对于强调功能行为的面向过程变成方式而言,面向对象强调的是功能的封装,形成具备一定功能的对象。面向对象的思维方式,符合人们的思考习惯,可以将复杂的事情简单化。从面向过程到面向对象,程序员完成了从执行者到指挥者的角色转变。

在使用面向对象概念的时候:
1.先找具有所需要功能的对象是否存在,存在即可使用;
2.如果不存在,那么创建一个具备所需功能的对象;
3.创建具备不用功能对象的过程,就是简化开发,提高代码复用性的过程

二、面向对象的特征:

  1. 1.封装:Java的封装是一种信息隐藏技术,将属性和方法封装在一个类当中,只对调用者开放相应接口来访问该类的成员属性和方法。封装提高了程序的复用性和可维护性;

  1. 2.继承:Java的继承特性,能使一个派生类类拥有其父类的属性和方法,并扩展其特有的方法;

  1. 3.多态:指允许不同类的对象对同一方法做出不同响应。即同一方法可以根据调用对象的不同而表现出多种不同的行为方式。多态的发生必须满足1)继承;2)复写;3)父类引用指向子类对象;这三个条件

三、类与对象的关系

  1. Java中通过定义类的形式,来描述生活中的具体事物。类是具体事物的抽象定义。对象则是该类在生活中的具体体现,是实实在在的个体。

这里写图片描述

四、类的定义

  1. 生活中描述事物,就是要描述事物的不同属性和行为。如:人有身高,体重,年龄等属性;有起床,刷牙等行为。在Java中,用类 (class)来描述事物。

属性:就是类中声明的成员变量;
行为:就是类中声明的成员方法;

  1. 定义一个类,其实就是在定义类中的成员变量和成员方法。

类的示例代码:

class Car {
    String color = "red";
    int num = 4;

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

创建对象的代码示例:

class Test {
    public static void main(String[] args) {
        Car c1 = new Car();
        c1.color = "blue";
        Car c2 = new Car();
    }
}

该对象的内存结构图如下:

这里写图片描述

new一个Car对象的时候:

  1. 1.在堆内存中开辟空间,储存 Car()对象及其成员变量 colornum
    2.将内存地址赋给栈内存中该对象的引用 c1c1即指向 Car()对象;
    3. c1修改 color的值,将新值赋给 color

五、成员变量和局部变量的不同

  1. 1.作用域不同:
    1. 1)成员变量作用域为整个类;
      2)局部变量作用域为方法或者语句

  1. 2.在内存中的位置不同:
    1. 1)成员变量在堆内存中;对象实例被new出来之后,才开辟内存空间给成员变量;
      2)局部变量在栈内存中

六、匿名对象:

  1. 1.应用一:

new Car().num = 5;
new Car().color = "green";
new Car().run();

如果使用多个匿名对象,第一行执行完之后,由于没有引用指向这个对象,该对象立刻成为垃圾,等待回收。

这里写图片描述

用匿名对象调用方法是通常做法,调用成员属性没有意义。所以,当对对象方法只调用一次时,可以用匿名对象完成。如果对对象进行多个成员调用,必须给这个对象起名字。

  1. 2.应用二:
    1. 将匿名对象作为实际参数进行传递。

不使用匿名对象作为参数的示例代码:

class Test {
    public static void main(String[] args) {
        Car c = new Car();
        show(c);
    }

    public static void show(Car c) {
        c.num = 3;
        c.color = "black";
        c.run();
    }
}

上述代码内存运行过程如图:

这里写图片描述

运行过程:

  1. 1.将创建的对象的内存地址赋给引用 cc即指向该对象;
    2.将引用 c传给该对象的 show()方法,即将该对象的地址赋给 show()方法的参数 cshow()方法中的参数 c即指向该对象;
    3. show()方法访问该对象的成员变量

使用匿名对象作为参数的示例代码:

public static void main(String[] args) {
    show(new Car());
}

public static void show(Car c) {
    c.num = 3;
    c.color = "black";
    c.run();
}

上述代码内存运行过程如图:

这里写图片描述

使用匿名对象作为参数,直接将对象地址赋给show()方法的参数cc再访问该对象的成员变量。

七、封装

  1. 1.定义
    1. 封装是指,隐藏对象的属性和实现细节,仅对外提供公共访问方式。

  1. 2.封装的好处
    1. 1)类的内部变化被隔离,调用者无须知道这些变化,照样能实现功能;
      2)类内部细节的实现无须调用者了解,便于使用;
      3)封装的代码可以被不同调用者使用,提高复用性;
      4)不想对外开放的属性,通过封装能很好隐藏起来,提高安全性

  1. 3.private关键字
    1. private:用于修饰类中的成员变量和成员方法,被修饰的成员只能在本类被访问。 private是封装的一种表现形式。当使用 private修饰成员变量后,应设置相应的 getset方法,让外部访问私有变量。

示例代码:

class Person {
    private String name;
    private int age;

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

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

    public void getName() {
        return name;
    }

    public void getAge() {
        return age;
    }
}

八、构造方法

  1. 1.构造方法特点
    1. 1)方法名与类名相同;
      2)不用定义返回值类型;
      3)不可以写 return语句;
      4)在创建对象的时候,自动调用

  1. 2.构造方法的作用
    1. 构造方法可以对对象进行初始化。对象在创建的时候,就应该具备其基本特性。构造方法就初始化了这些基本特性。

  1. 3.构造方法小细节
    1. 1)当一个类中没有定义构造方法时,Java默认会为该类加入一个空参数的构造方法;
      2)当在类中声明了自定义的构造方法后,Java不再为该类添加构造方法;
      3)对象创建时就具备的特性和行为,可以定义在构造函方法中,以便创建时初始化;
      4)构造方法中初始化过了的私有变量,还是需要定义 setget方法,因为在对象的使用过程中,可能涉及改变其私有变量的值,此时就要用到 set方法,获取该私有变量的值,需要用到get方法

示例代码:

class Person {
    private String name;
    private int age;

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

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

    public void getName() {
        return name;
    }

    public void getAge() {
        return age;
    }

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

class Test {
    public static void main(String[] args) {
        Person p = new Person("Lucy", 20);
        p.setName("Linda");
    }
}

九、构造代码块

  1. 1.构造代码块的定义
    1. 构造代码块是定义在类中的一个独立代码区间。构造代码块的作用是为对象进行初始化。对象一旦创建就立刻运行构造代码块,且运行优先级高于该类的构造方法。

  1. 2.构造代码和构造方法的区别
    1. 1)构造代码是给所有对象进行统一初始化;
      2)构造方法是给对应的对象初始化

  1. 3.构造代码块的应用
    1. 将所有对象的共性属性或行为定义在构造代码块中,那么所有对象创建的时候同一执行构造代码块中的内容。

示例代码:

class Person {
    private String name;
    private int age;

    {
        this.name = "Travis";
        this.age = 20;
    }

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

十、this关键字

  1. 1.this的概念
    1. this关键字代表调用 this所在方法的对象的引用,即哪个对象调用了 this所在的方法, this就指向那个对象。

  1. 2.this关键字的应用
    1. 1) this关键字用于区分局部变量和成员变量重名的情况;并且,定义类中方法时,如果需要在方法内部使用本类对象的时候,用 this指向这个对象

示例代码:

package test;

public class Animal {
    private String type;
    private int leg;

    Animal(String type, int leg) {
        this.type = type; //this区分了成员变量和局部变量,并代表本类的对象
        this.leg = leg;
    }
}

    1. 2)this关键字可以用于构造方法间互相调用

示例代码:

package test;

public class Animal {
    private String type;
    private int leg;

    Animal(String type) {
        this.type = type; //this区分了成员变量和局部变量,并代表本类的对象
    }

    Animal(String type, int leg) {
        this(type); //this关键字以type为参数调用了第一个构造函数
        this.leg = leg;
    }
}

    1. 3)注意: this关键字调用其他构造方法的语句只能写在构造方法的第一行,因为初始化动作要先执行

十一、static关键字

  1. 1.static关键字基本用法
    1. static关键字是一个修饰符,用于修饰成员变量和成员方法。被 static修饰的成员,称为静态成员,或类成员。当一个成员是所有对象所共享的时候,用 static关键字修饰,那么每次创建对象的时候,该成员就不会被初始化在堆内存中。

class Person {
    String name;
    static String country = "CN";
}

上述代码中,country被声明成静态变量,则该变量不再存在于堆内存中,而作为共享数据储存于方法区,如下图:

这里写图片描述

  1. 2.static关键字的特点
    1. 1)随着类的加载而加载
      1. 类一旦加载到内存, static成员就已经在内存中被分配空间。同时, static成员随着类的消失而消失,也就是说, static成员的生命周期最长

    1. 2)优先于对象存在
      1. 静态随着类的加载而存在,对象创建后才存在

    1. 3)被所有对象共享
      1. 所有对象共享一份静态成员

    1. 4)可以直接被类名所调用
      1. 由于类刚加载的时候,还没有对象,而静态成员已经加载到内存,所以可以用类名直接调用

  1. 3.静态变量和成员变量的区别
    1. 1)存放位置不同
      1. 静态变量随着类的加载而存在与方法区中
        成员变量随着对象的创建而存在于堆内存中

    1. 2)生命周期不同
      1. 静态变量生命周期等于类的生命周期,随着类存在消亡
        成员变量生命周期随着对象存在消亡,生命周期较静态变量短

  1. 4.static关键字注意事项
    1. 不能把所有成员都定义成 static,因为:
      1. 1)不是所有成员都能被所有对象共享;
        2)定义成 static的成员,生命周期随着类存在消亡,对象使用完毕,这些成员还存在于内存,浪费资源;
        3)静态方法中不能引用非静态成员,即静态方法只能访问静态成员(非静态方法可以访问静态成员);
        4)静态方法中不能出现 thissuper关键字

  1. 5.静态的利弊
    1. 利:
      1. 1)存储所有对象共享的成员于方法区,节省资源;
        2)可以直接被类名调用,使用简单

    1. 弊:
      1. 1)生命周期过长,可能出现浪费资源的情况;
        2)访问出现局限性(静态方法只能访问静态成员)

  1. 6.使用static关键字的时机
    1. 1)当对象中出现需要共享的数据时,使用 static关键字。这里的数据,指的是成员的值或功能,不是成员本身。成员本身称为共享的属性,而不是共享的数据,如 name成员变量,是 Person类的对象的共享属性,而非共享数据;

    1. 2)当成员方法没有对类中任何非静态成员变量进行操作的时候,该方法可以定义成静态成员方法

  1. 7.静态的应用
    1. 定义了 ArrayTools类,包含各种操作数组的方法。由于所有的方法都没有操作该类中的成员变量,根据 static关键字使用原则,声明该类中所有方法为静态成员方法。

示例代码:

package test;

/**
 * 利用ArrayTools类实现冒泡排序,选择排序,普通查找和二分查找
 * @author jeremy
 *
 */

public class TestArrayTools {
    public static void main(String[] args) {
        int[] arr = new int[]{3, 98, 56, 4, 10, 66, 27, 17};

        //冒泡排序
        System.out.println("Bubble Sort:"); 
        ArrayTools.bubbleSort(arr);
        ArrayTools.printArray(arr);
        System.out.println("----------------------------------------------");

        //选择排序
        System.out.println("Select Sort:");
        ArrayTools.selectSort(arr);
        ArrayTools.printArray(arr);
        System.out.println("----------------------------------------------");

        //选择排序2
        System.out.println("Select Sort Version 2:");
        ArrayTools.selectSort_2(arr);
        ArrayTools.printArray(arr);
        System.out.println("----------------------------------------------");

        //普通查找
        System.out.println("Search array for 10:");
        System.out.println(ArrayTools.search(arr, 10));
        System.out.println("----------------------------------------------");

        System.out.println("Search array for 11:");
        System.out.println(ArrayTools.search(arr, 11));
        System.out.println("----------------------------------------------");

        //二分查找
        System.out.println("Half Search array for 27:");
        System.out.println(ArrayTools.halfSearch(arr, 27));
        System.out.println("----------------------------------------------");

        System.out.println("Half Search array for 99:");
        System.out.println(ArrayTools.halfSearch(arr, 99));
        System.out.println("----------------------------------------------");

        //二分查找2
        System.out.println("Half Search array Version 2 for 56:");
        System.out.println(ArrayTools.halfSearch(arr, 56));
        System.out.println("----------------------------------------------");

        System.out.println("Half Search array Version 2 for -9:");
        System.out.println(ArrayTools.halfSearch(arr, -9));
        System.out.println("----------------------------------------------");

    }
}

class ArrayTools {

    /**
     * 交换数组中两个值的位置
     * @param arr 接受数组作为第一参数
     * @param a 接受整型数据作为第二参数,用于标识数组元素
     * @param b 接受整型数据作为第三参数,用于标识数组元素
     */
    private static void swap(int[] arr, int a, int b) {
            int tmp = arr[b];
            arr[b] = arr[a];
            arr[a] = tmp;
    }

    /**
     * 打印数组的方法
     * @param arr 接受数组作为参数
     */
    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] + "]");
            }
        }
    }

    /**
     * 冒泡排序,相邻两书比较,小的数左移
     * @param arr 接受数组作为参数
     */
    public static void bubbleSort(int[] arr) {
        for(int x = 0; x < arr.length; x ++) {
            for(int y = 0; y < arr.length - x - 1; y ++) {
                if(arr[y] > arr[y + 1]) {
                    swap(arr, y, y + 1);
                }
            }
        }
    }

    /**
     * 选择排序一,前一个数和后一个做比较,若大于后一个数,则立刻交换位置
     * @param arr 接受数组作为参数
     */
    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);
                }
            }
        }
    }

    /**
     * 选择排序二,前一个数和后一个做比较,如果大于后一个,则交换下标值,让小的数继续比较
     * 如果较小数的下标发生过变化,则和原数交换位置
     * @param arr 接受数组作为参数
     */
    public static void selectSort_2(int[] arr) {
        int k;
        for(int x = 0; x < arr.length - 1; x ++) {
            k = x;
            for(int y = x + 1; y < arr.length; y ++) {
                if(arr[k] > arr[y]) {
                    k = y;
                }
            }
            if(k != x) {
                int tmp = arr[k];
                arr[k] = arr[x];
                arr[x] = tmp;
            }
        }
    }

    /**
     * 普通查找,遍历数组,将每个元素和目标值作比较
     * @param arr 接受数组作为第一参数
     * @param key 接受整型目标值作为第二参数
     * @return 匹配成功,返回下标;匹配失败,返回-1
     */
    public static int search(int[] arr, int key) {
        for(int x = 0; x < arr.length; x ++) {
            if(arr[x] == key) {
                return x;
            }
        }
        return -1;
    }

    /**
     * 二分法查找一
     * @param arr 接受数组作为第一参数
     * @param key 接受整型目标值作为第二参数
     * @return 匹配成功,返回下标;匹配失败,返回-1
     */
    public static int halfSearch(int[] arr, int key) {
        int min = 0;
        int max = arr.length - 1;
        int mid = (min + max) / 2;

        while(arr[mid] != key) {
            if(arr[mid] < key) {
                min = mid + 1;
            } else {
                max = mid - 1;
            }
            if(min > max) {
                return -1;
            }
            mid = (min + max) / 2;
        }
        return mid;
    }

    /**
     * 二分法查找二
     * @param arr 接受数组作为第一参数
     * @param key 接受整型目标值作为第二参数
     * @return 匹配成功,返回下标;匹配失败,返回-1
     */
    public static int halfSearch_2(int[] arr, int key) {
        int min = 0;
        int max = arr.length - 1;
        int mid = (min + max) / 2;

        while(min <= max) {
            if(arr[mid] < key) {
                min = mid + 1;
            } else if(arr[mid] > key) {
                max = mid - 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
}

  1. 8.静态代码块
    1. 1)格式

static {
statement;
}

    1. 2)特点
      1. 随着类的加载而执行,并且只执行一次。用于对类进行初始化。

示例代码:

class TestStatic {

    TestStatic() {
        System.out.print("b ");
    }

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

    {
        System.out.print("c ");
    }

    TestStatic(int x) {
        System.out.print("d ");
    }
}

class TestStaticMain {
    public static void main(String[] args) {
        new TestStatic(5);
    }
}

上述代码的输出结果为:a c d

  1. 执行过程为:
    1. 1> 先执行 static代码块,由于其要初始化类,优先级最高,输出 a
      2> 后执行构造代码块,由于其要初始化对象,优先级介于构造方法和静态代码块间,输出 c
      3> 最后执行相应的构造方法,输出 d

注意:
静态代码块中不能访问非静态成员变量,构造代码块可以访问非静态变量。

示例代码:

class TestStatic {

    int num = 9;

    static {
        //错误,静态代码块先于非静态成员存在,无法访问非静态成员
        System.out.print("a " + num); 
    }

    {
        //正确,构造代码块初始化本类对象,可以访问该类的成员变量
        System.out.print("c " + num);
    }
}

class TestStaticMain {
    public static void main(String[] args) {
        new TestStatic();
    }
}

输出:c9

  1. 9.主函数的属性
    1. 主函数的定义:
      1. 主函数是一个特殊函数,作为程序的入口,被jvm调用。主函数格式固定。

      1. public:主函数无访问限制
        static:主函数为静态,随着类的加载就存在于方法区
        void:主函数没有返回值
        mian:不是关键字,是能被jvm识别的特殊标识符
        String[] args:函数的参数,参数类型为字符串数组

十二、对象的初始化过程

  1. 1.对象的初始化过程如下:

这里写图片描述

1)通过虚拟机,加载类的class文件到内存;
2)执行方法区静态代码块(如有),对类进行初始化;
3)在堆内存中开辟对象new Person(),及其类成员变量的储存空间,并对成员变量进行初始化;在栈内存中开辟main方法和该对象引用的储存空间;
4)对类成员变量进行显式初始化(如有);
5)执行构造代码块(如有),对对象进行初始化;
6)执行构造方法,调用相应的构造方法对成员变量进行初始化;
7)将内存地址赋给栈内存中该对象的引用

  1. 2.对象调用非静态成员方法过程
    1. 1)从方法区将要调用的非静态方法加载到栈内存;
      2)将该对象的引用的内存地址,赋给要调用的方法中自带的this关键字,此时,this指代的就是该对象的引用;
      3)内存地址赋值完毕,this指向该对象,将栈内存中变量的值,赋给堆内存中对象的成员变量,完成赋值,结束对非静态方法的调用

十三、设计模式之单例设计模式

  1. 设计模式是解决某一类问题最行之有效的方法。作为Java23种设计模式中的一种,单例设计模式解决了一个类在内存中只能存在一个对象的问题,即保证一个类,在内存中只有一个对象。

  1. 想要保证对象唯一性:
    1. 1.禁止其他程序建立该类的对象;
      2.在该类中自定义一个对象;
      3.开放端口,让其他程序访问该对象

  1. 实现:
    1. 1.私有化构造方法;
      2.在该类中创建一个本来对象;
      3.提供方法获取该对象

饿汉式:

class Single {

    //私有化构造方法,其他程序不能创建该类对象
    private Single() {}

    //在本类中,创建静态的本类对象
    private static Single s = new Single();

    //对其他程序提供静态方法,获取该类对象
    public static Single getInstance() {
        return s;
    }
}

class TestSingle {
    public static void main(String[] args) {
        //调用Single类的静态方法,获取Single对象
        Single s = Single.getInstance();
    }
}

单例模式内存图:

这里写图片描述

图示说明:

  1. 1. Single类在加载的时候,其静态成员变量 s,静态成员函数 getInstance()以及构造函数都已加载到方法区;在 Single类中创建 Single对象的时候,已经将 Single对象的内存地址赋给方法区中的静态变量 s

  1. 2. mian()方法中调用了 getInstance()方法后,返回的 Single对象 sSingle对象的内存地址赋给栈内存中的变量 s,即栈内存中的变量 s现在指向堆内存中的 Single对象,是 Single对象的引用;

  1. 3.由于 Single类的构造方法被声明为私有,则外部程序无法创建该类的对象;若有代码 Single s1 = Single.getInstance();则继续执行上述两个步骤,结果是 s1仍然指向堆内存中的同一 Single对象,这保证了 Single对象的唯一性

懒汉式:

class Single {

    //私有化构造方法,其他程序不能创建该类对象
    private Single() {}

    //延迟创建本类对象
    private static Single s = null;

    //对其他程序提供静态方法,获取该类对象
    public static Single getInstance() {
        //判断,如果s为空,则创建本类对象
        if(s == null) {
            s = new Single();
        }
        //返回该对象
        return s; 
    }
}

饿汉式和懒汉式的不同:

  1. 饿汉式: Single类一旦加载到内存,就马上创建对象
    懒汉式: Single类加载到内存时,对象没有被创建。只有调用了 getInstance()方法时,才创建对象

开发时,用饿汉式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值