美文网首页Java-Python-Django社区程序员
【JavaSE(三)】Java面向对象(上)

【JavaSE(三)】Java面向对象(上)

作者: 苍云横渡 | 来源:发表于2018-05-10 11:38 被阅读73次

原文地址:https://www.cloudcrossing.xyz/post/35/

面向对象是基于面向过程的编程思想。

  • 面向过程:强调的是每一个功能的步骤;
  • 面向对象:强调的是对象,然后由对象去调用功能。

Java程序的开发,设计和特征:

  • 开发:就是不断的创建对象,通过对象调用功能
  • 设计:就是管理和维护对象间的关系
  • 特征:封装,继承,多态

如何让我们的操作更符合面向对象思想呢?

  • 有哪些类?
  • 每个类有哪些成员?
  • 类与类的关系?

1 类与对象

如何描述现实世界的事物,有两点:

  • 属性:事物的基本描述
  • 行为:事物的功能

Java语言中最基本的单位是类。所以,我们要用类来体现事物。

  • 成员变量:事物属性
  • 成员方法:事物行为

类:是一组相关的属性和行为的集合。是一个抽象的概念。

对象:是该类事物的具体存在,是一个具体的实例。举例:学生是一个类,班长是一个学生类对象。


2 类的定义及使用

类的定义:

  • 成员变量:定义格式和以前一样,就是位置不同,在类中,方法外。
  • 成员方法:定义格式和以前一样,就是去掉了static。

使用类的内容:

  • 创建对象。格式:类名 对象名 = new 类名();
  • 使用成员变量。格式:对象名.成员变量
  • 成员方法。格式:对象名.成员方法()
class Phone {
    //品牌
    String brand;
    //价格
    int price;
    //颜色
    String color;
    
    //打电话的方法
    public void call(String name) {
        System.out.println("给"+name+"打电话");
    }
    
    //发短信的方法
    public void sendMessage() {
        System.out.println("群发短信");
    }
    
    //玩游戏的方法
    public void playGame() {
        System.out.println("玩游戏");
    }
}

class PhoneDemo {
    public static void main(String[] args) {
        //创建手机对象
        //类名 对象名 = new 类名();
        Phone p = new Phone();
        
        //直接输出成员变量值
        System.out.println(p.brand+"---"+p.price+"---"+p.color);
        
        //给成员变量赋值
        p.brand = "诺基亚";
        p.price = 100;
        p.color = "灰色";
        //再次输出
        System.out.println(p.brand+"---"+p.price+"---"+p.color);
        
        //调用方法
        p.call("林青霞");
        p.sendMessage();
        p.playGame();
    }
}
//运行结果:
null---0---null
诺基亚---100---灰色
给林青霞打电话
群发短信
玩游戏

一个对象的内存图

二个对象的内存图

三个对象的内存图

可以看到,多个对象的方法的地址都是相同的(比如0x001)。


3 成员变量和局部变量的区别

(1)在类中的位置不同

  • 成员变量:类中方法外
  • 局部变量:方法定义中或者方法声明上

(2)在内存中的位置不同

  • 成员变量:在堆中 heap
  • 局部变量:在栈中 stack

(3)生命周期不同

  • 成员变量:随着对象的创建而存在,随着对象的消失而消失
  • 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失

(4)初始化值不同

  • 成员变量:有默认值
  • 局部变量:没有默认值,必须定义,赋值,然后才能使用

注意:局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则

class Varialbe {
    int num = 10; //成员变量
    public void show() {    
        int num = 100; //局部变量
        System.out.println(num);
    }
}

class VariableDemo {
    public static void main(String[] args) {
        Varialbe v = new Varialbe();
        System.out.println(v.num); //访问成员变量
        v.show();   //访问局部变量
    }
}
#运行结果
10
100

4 类作为形式参数的问题

回忆一下,形式参数的问题:

  • 基本类型:形式参数的改变不影响实际参数
  • 引用类型:形式参数的改变直接影响实际参数

如果你看到了一个方法的形式参数是一个类类型(引用类型),这里其实需要的是该类的对象

//形式参数是基本类型
class Demo {
    public int sum(int a,int b) {
        return a + b;
    }
}

//形式参数是引用类型
class Student {
    public void show() {
        System.out.println("我爱学习");
    }
}

class StudentDemo {
    //如果你看到了一个方法的形式参数是一个类类型(引用类型),这里其实需要的是该类的对象。
    public void method(Student s) { //调用的时候,把main方法中的s的地址传递到了这里 Student s = new Student();
        s.show();
    }
}

class ArgsTest {
    public static void main(String[] args) {
        //形式参数是基本类型的调用
        Demo d = new Demo();
        int result = d.sum(10,20);
        System.out.println("result:"+result);
        System.out.println("--------------");
        
        //形式参数是引用类型的调用
        StudentDemo sd = new StudentDemo();
        Student s = new Student();
        sd.method(s); //把s的地址给到了这里
    }
}
#运行结果
result:30
--------------
我爱学习

5 匿名对象

匿名对象就是没有名字的对象。

应用场景:

  • 调用方法,仅仅只调用一次的时候。
  • 可以作为实际参数传递。匿名对象调用完毕就是垃圾,可以被垃圾回收器回收
  • 还可以将 匿名对象作为实际参数 传递到 匿名对象的方法调用中
class Student {
    public void show() {
        System.out.println("我爱学习");
    }
}

class StudentDemo {
    public void method(Student s) {
        s.show();
    }
}

class NoNameDemo {
    public static void main(String[] args) {
        //带名字的调用
        Student s = new Student();
        s.show();
        System.out.println("--------------");
        
        //匿名对象
        //new Student();
        //匿名对象调用方法
        new Student().show();
        new Student().show(); //这里其实是重新创建了一个新的对象
        System.out.println("--------------");
        
        //匿名对象作为实际参数传递
        StudentDemo sd = new StudentDemo();
        sd.method(new Student());
        //匿名对象作为实际参数 传递到 匿名对象的方法
        new StudentDemo().method(new Student());
    }
}
#运行结果
我爱学习
--------------
我爱学习
我爱学习
--------------
我爱学习
我爱学习

6 封装和private关键字

6.1 封装

(1)封装的概述:隐藏实现细节,仅对外提供公共的访问方式。

(2)好处:

  • 隐藏实现细节,提供公共的访问方式
  • 提高代码的复用性
  • 提高代码的安全性

(3)设计原则:

  • 将不需要对外提供的内容都隐藏起来
  • 把属性隐藏,提供公共方法对其访问

(4)private 是封装的一种体现。

6.2 private关键字

(1)是一个权限修饰符,可以修饰成员变量和成员方法

(2)特点:被private修饰的后的成员只能在本类中被访问

class Demo {
    //用private修饰
    private int num = 10;
    
    public void show() {
        System.out.println(num);
    }
    
    private void method() {
        System.out.println("method");
    }
    
    public void function() {
        method();
    }
}

class PrivateDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        //不能访问私有的成员变量
        //System.out.println(d.num);
        d.show();
        //不能访问私有的成员方法
        //d.method();
        d.function(); //输出:method
    }
}

(3)private的应用:把所有的成员变量用private修饰,并提供对应的getXxx()/setXxx()方法

class Student {
    //姓名
    private String name;
    //年龄
    private int age;
    
    //姓名获取值
    public String getName() {
        return name;
    }
    
    //姓名设置值
    public void setName(String n) {
        name = n;
    }
    
    //年龄获取值
    public int getAge() {
        return age;
    }
    
    //年龄赋值
    public void setAge(int a) {
        age = a;
    }
}

class StudentTest {
    public static void main(String[] args) {
        //创建学生对象
        Student s = new Student();
        
        //使用成员变量
        //错误:被私有修饰了,外界不能直接访问了
        //System.out.println(s.name+"---"+s.age);
        System.out.println(s.getName()+"---"+s.getAge());
        
        //给成员变量赋值
        //s.name = "林青霞";
        //s.age = 27;
        //通过方法给赋值
        s.setName("林青霞");
        s.setAge(27);
        System.out.println(s.getName()+"---"+s.getAge());
    }
}
//运行结果
null---0
林青霞---27

7 this关键字

代表当前类的引用对象。记住:哪个对象调用方法,该方法内部的this就代表那个对象。

this的应用场景:

  • 解决了局部变量隐藏成员变量的问题
  • 其他用法后面和super一起讲解
/*
    this的场景:
        解决局部变量隐藏成员变量
*/
//定义学生类
class Student {
    //姓名
    private String name;
    //年龄
    private int age;
    
    //姓名获取值
    public String getName() {
        return name;  //这里其实是隐含了this
    }
    
    //姓名设置值
    public void setName(String name) { //name = "林青霞";
        //name = name; //输出:null---27
        //变量的使用规则:就近原则,这里相当于将自己赋值给自己,和外界无关

        //Student.name = name;  //报错“错误: 无法从静态上下文中引用非静态 变量 name”
        //这里是类名,目前还没有说过类似的用法,所以这个是有问题的
        //这里的调用只能通过对象名
        //这个对象如果存在,它应该代表的是Student的一个对象。
        //那么,谁能够代表当前类的对象呢? java就提供了一个关键字 this
        this.name = name;
    }
    
    //年龄获取值
    public int getAge() {
        return age;  //这里其实是隐含了this
    }
    
    //年龄赋值
    public void setAge(int age) {
        this.age = age;
    }
}

//测试类
class StudentTest {
    public static void main(String[] args) {
        //创建学生对象
        Student s = new Student();
        //给成员变量赋值
        s.setName("林青霞");
        s.setAge(27);
        //获取数据
        System.out.println(s.getName()+"---"+s.getAge());
    }
}

8 构造方法

(1)作用:用于对对象的数据进行初始化。

(2)格式:

  • 方法名和类名相同
  • 没有返回值类型以及返回值
  • 不能有void修饰

(3)构造方法的注意事项:

  • 如果没有给出构造方法,系统默认提供一个无参构造方法
  • 如果给出了构造方法,系统将不再提供默认构造方法(推荐:永远手动自己给出无参构造方法)
  • 构造方法也可以重载

(4)给成员变量赋值的方式:

  • 无参构造方法+setXxx()
  • 带参构造方法

(5)标准类的写法案例:

    学生类:
        成员变量:
            name,age
        构造方法:
            无参,带两个参
        成员方法:
            getXxx()/setXxx()
            show():输出该类的所有成员变量值
            
    给成员变量赋值:
        A:setXxx()方法
        B:构造方法
        
    输出成员变量值的方式:
        A:通过getXxx()分别获取然后拼接
        B:通过调用show()方法搞定
class Student {
    //姓名
    private String name;
    //年龄
    private int age;
    
    //构造方法
    public Student() {
    }
    
    public Student(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 void show() {
        System.out.println(name+"---"+age);
    }
}

//测试类
class StudentTest {
    public static void main(String[] args) {
        //方式1给成员变量赋值
        //无参构造+setXxx()
        Student s1 = new Student();
        s1.setName("林青霞");
        s1.setAge(27);
        //输出值
        System.out.println(s1.getName()+"---"+s1.getAge());
        s1.show();
        System.out.println("----------------------------");
        
        //方式2给成员变量赋值
        Student s2 = new Student("刘意",30);
        System.out.println(s2.getName()+"---"+s2.getAge());
        s2.show();
    }
}

(6)类的初始化过程

Student s = new Student();做了哪些事情?

  • a:把Students.class文件加载到内存
  • b:在栈内存为s开辟空间
  • c:在堆内存为学生对象申请空间
  • d:给学生对象的成员变量进行默认初始化。null,0
  • e:对象构造完毕,把地址赋值给s变量

9 static关键字

9.1 static关键字概述

(1)static可以用来修饰类的成员方法、类的成员变量。static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。另外可以编写static代码块来优化程序性能。

(2)静态的特点:

  • A:随着类的加载而加载
  • B:优先与对象存在
  • C:被类的所有对象共享,节省空间
  • D:可以通过类名调用

(3)静态的注意事项:

  • 在静态方法中没有this对象
  • 静态只能访问静态

(4)静态的内存图

class Person {
    //姓名
    String name;
    //年龄
    int age;
    //国籍
    //String country;
    static String country;
    
    public Person(){}
    
    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    
    public Person(String name,int age,String country) {
        this.name = name;
        this.age = age;
        this.country = country;
    }
    
    public void show() {
        System.out.println("姓名:"+name+",年龄:"+age+",国籍:"+country);
    }
}

class Helloworld {
    public static void main(String[] args) {
        //创建对象1
        Person p1 = new Person("邓丽君",16,"中国");
        p1.show();
        
        //创建对象2
        Person p2 = new Person("杨幂",22);
        p2.show();
        
        //创建对象3
        Person p3 = new Person("凤姐",20);
        p3.show();
        
        p3.country = "美国";
        p3.show();
        
        p1.show();
        p2.show();
    }
}
//运行结果:
姓名:邓丽君,年龄:16,国籍:中国
姓名:杨幂,年龄:22,国籍:中国
姓名:凤姐,年龄:20,国籍:中国
姓名:凤姐,年龄:20,国籍:美国
姓名:邓丽君,年龄:16,国籍:美国
姓名:杨幂,年龄:22,国籍:美国

静态的内容在方法区的静态区。

9.2 静态方法

static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。

但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。

因此,如果说想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。

main方法的格式讲解:public static void main(String[] args) {...}

  • public:公共的,访问权限是最大的。由于main方法是被jvm调用,所以权限要够大。
  • static:静态的,不需要创建对象,通过类名就可以。方便jvm的调用。
  • void:因为我们曾经说过,方法的返回值是返回给调用者,而main方法是被jvm调用。返回内容给jvm没有意义。
  • main:是一个常见的方法入口。我见过的语言都是以main作为入口。
  • String[] args:这是一个字符串数组。早期是为了接收键盘录入的数据的。

9.3 静态变量

static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

static成员变量的初始化顺序按照定义的顺序进行初始化。

举一个访问静态变量的例子。

class Student {
    //非静态变量
    int num = 10;
    
    //静态变量
    static int num2 = 20;
}

class Helloworld {
    public static void main(String[] args) {
        Student s = new Student();
        System.out.println(s.num); //输出:10
        
        System.out.println(Student.num2);  //输出:20
        System.out.println(s.num2);  //输出:20
    }
}

总结一下,静态变量和成员变量的区别:

  • 所属不同
    • 静态变量:属于类,类变量
    • 成员变量:属于对象,对象变量,实例变量
  • 内存位置不同
    • 静态变量:方法区的静态区中
    • 成员变量:堆内存
  • 生命周期不同
    • 静态变量:静态变量随着类的加载而加载,随着类的消失而消失
    • 成员变量:成员变量是随着对象的创建而存在,随着对象的消失而消失
  • 调用不同
    • 静态变量:可以通过对象名调用,也可以通过类名调用
    • 成员变量:可以通过对象名调用,也可以通过类名调用

9.4 静态代码块

static关键字还有一个比较关键的作用就是用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。

为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次

//普通类
class PuTong {
    public PuTong(){
        System.out.print("默认构造方法!-->");
    }

    //非静态代码块
    {
        System.out.print("非静态代码块!-->");
    }

    //静态代码块
    static{
        System.out.print("静态代码块!-->");
    }

    public static void test(){
        System.out.println("普通方法中的代码块!");
    }
}

//测试类
public class Helloworld {
    public static void main(String[] args) {
        PuTong c1 = new PuTong();
        c1.test();

        PuTong c2 = new PuTong();
        c2.test();
    }
}
//运行结果:
静态代码块!-->非静态代码块!-->默认构造方法!-->普通方法中的代码块!
非静态代码块!-->默认构造方法!-->普通方法中的代码块!

10 代码块

(1)用{}括起来的代码。

(2)分类:

  • 局部代码块:用于限定变量的生命周期,及早释放,提高内存利用率
  • 构造代码块:在类中的成员位置,用{}括起来的代码。每次调用构造方法执行前,都会先执行构造代码块
    • 作用:把多个构造方法中相同的代码可以放到这里
  • 静态代码块:在类中的成员位置,用{}括起来的代码,只不过它用static修饰了
    • 作用:对类的数据进行初始化,仅仅只执行一次

(3)静态代码块,构造代码块,构造方法的顺序:静态代码块 > 构造代码块 > 构造方法

class Student {
    static {
        System.out.println("Student 静态代码块");
    }
    
    {
        System.out.println("Student 构造代码块");
    }
    
    public Student() {
        System.out.println("Student 构造方法");
    }
}

class Helloworld {
    static {
        System.out.println("林青霞都60了,我很伤心");
    }
    
    public static void main(String[] args) {
        System.out.println("我是main方法");
        System.out.println("---------------");
        Student s1 = new Student();
        System.out.println("---------------");
        Student s2 = new Student();
    }
}
//运行结果:
林青霞都60了,我很伤心
我是main方法
---------------
Student 静态代码块
Student 构造代码块
Student 构造方法
---------------
Student 构造代码块
Student 构造方法

11 工具类与制作帮助文档

11.1 工具类

先来看一段代码。

class ArrayDemo {
    public static void main(String[] args) {
        //定义数组
        int[] arr = {28,55,37,46,19};
        
        //需求:遍历数组
        for(int x=0; x<arr.length; x++) {
            if(x == arr.length-1) {
                System.out.println(arr[x]);
            }else {
                System.out.print(arr[x]+", ");
            }
        }   
}

如果我有多个数组都要进行遍历,那么代码的重复度就很高,如何改进呢?用方法改进。

class ArrayDemo {
    public static void main(String[] args) {
        //定义数组
        int[] arr = {28,55,37,46,19};
        
        //需求:遍历数组
        //调用非静态方法
        //ArrayDemo ad = new ArrayDemo();
        //ad.printArray(arr);

        //调用静态方法
        printArray(arr);
    }

    public static void printArray(int[] arr) {
        for(int x=0; x<arr.length; x++) {
            if(x == arr.length-1) {
                System.out.println(arr[x]);
            }else {
                System.out.print(arr[x]+", ");
            }
        }
    }

    //假设该方法不是静态的
    /*
    public void printArray(int[] arr) {
        for(int x=0; x<arr.length; x++) {
            if(x == arr.length-1) {
                System.out.println(arr[x]);
            }else {
                System.out.print(arr[x]+", ");
            }
        }
    }
    */
}

可以看到:

  • 如果printArray(int[] arr)为静态方法,则可以直接调用printArray(arr);(且main方法为静态的,只能访问静态方法)
  • 如果printArray(int[] arr)不是静态方法,则需要创建一个类的对象,通过调用对象的方法来使用。

测试类的作用是创建其他类的对象,调用其他类的功能。

而我们现在的操作是跟数组相关的,所以,应该把这些操作定义到数组操作类中。

//ArrayTool.java 与 (ArrayDemo.java同一文件夹下,就相当于定义在ArrayDemo.java中了)
class ArrayTool {
    
    //把构造方法私有,外界就不能在创建对象了
    private ArrayTool(){}

    public static void printArray(int[] arr) {
        for(int x=0; x<arr.length; x++) {
            if(x == arr.length-1) {
                System.out.println(arr[x]);
            }else {
                System.out.print(arr[x]+", ");
            }
        }
    }
}
//ArrayDemo.java 
class ArrayDemo {
    public static void main(String[] args) {
        //定义数组
        int[] arr = {28,55,37,46,19};
        
        //需求:遍历数组
        //定义一个数组的操作类
        //(在构造方法没有私有的情况下)
        //有了数组操作类之后的调用
        //ArrayTool at = new ArrayTool();
        //at.printArray(arr);
        
        //方法改进为静态后,就可以直接通过类名调用
        ArrayTool.printArray(arr);
    }
}

11.2 制作帮助文档

步骤:

  • A:写一个工具类
  • B:对这个类加入文档注释(/** */)
  • C:用工具解析文档注释:javadoc工具
  • D:格式:javadoc -d 目录 -author -version ArrayTool.java,目录:就可以写一个文件夹的路径

注意:

  • 制作帮助文档出错:找不到可以文档化的公共或受保护的类
  • 原因:类的权限不够,被文档化的类必须是public或protected的,私有类不会再文档中显示
//ArrayTool.java
/**
* 这是针对数组进行操作的工具类
* @author 苍云横渡
* @version V1.0
*/
public class ArrayTool {
    
    //把构造方法私有,外界就不能在创建对象了
    /**
    * 这是私有构造
    */
    private ArrayTool(){}

    /**
    * 这是遍历数组的方法,遍历后的格式是:[元素1, 元素2, 元素3, ...]
    * @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.println(arr[x]+"]");
            }else {
                System.out.print(arr[x]+", ");
            }
        }
    }
    
    /**
    * 这是获取数组中最大值的方法
    * @param  arr 这是要获取最大值的数组
    * @return 返回数组中的最大值
    */
    public static int getMax(int[] arr) {
        int max = arr[0];
        
        for(int x=1; x<arr.length; x++) {
            if(arr[x] > max) {
                max = arr[x];
            }
        }
        
        return max;
    }
    
    /**
    * 获取指定元素在数组中第一次出现的索引,如果元素不存在,就返回-1
    * @param arr 被查找的数组 
    * @param value 要查找的元素
    * @return 返回元素在数组中的索引,如果不存在,返回-1
    */
    public static int getIndex(int[] arr,int value) {
        int index = -1;
        
        for(int x=0; x<arr.length; x++) {
            if(arr[x] == value) {
                index = x;
                break;
            }
        }
        
        return index;
    }
}

生成如下文件

双击index.html可以看到

相关文章

网友评论

    本文标题:【JavaSE(三)】Java面向对象(上)

    本文链接:https://www.haomeiwen.com/subject/nyharftx.html