JAVA面向对象编程(上)

JAVA面向对象编程(上)(1)

---POP与OOP的差别、OOP三大特征、OOP两个要素、类和对象的使用、对象的内存解析、类中属性的使用、类中方法的声明和使用

  • 面向过程(POP)与面向对象(OOP)的差别

二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为,以函数为最小单位,考虑“怎么做?”。面向对象将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑“谁来做?”。

  • 面向对象的三大特征:

封装(Encapsulation)

继承(Inheritance)

多态(Polymorphism)

  • 面向对象的两个要素:

(Class)和对象(Object)是面向对象的核心概念。

类是对一类事物的描述,是抽象、概念上的定义

对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。”万事万物皆对象“

面向对象的重点就是类的设计,其实也就是类的成员的设计。

类的成员:

*属性=成员变量(field)

*方法=成员方法(method)

构造器

代码块

内部类

一、类和对象的使用

1.使用方法:

  1. 创建类、设计类的成员。
  2. 创建类的对象。
  3. 通过“对象.属性名“和”对象.方法名()“调用对象的结构。

代码实例:

public class OOPTest_Person {
    public static void main(String[] args) {
        //2.创建Person类的对象
        Person pers = new Person();
        //调用对象的结构:属性、方法
        //调用属性:对象.属性名
        pers.name = "马云";
        pers.age = 30;
        pers.isMale = true;
        //调动方法:对象.方法名()
        pers.eat();
        pers.sleep();
        pers.talk("中文");
        System.out.println("他的名字是" + pers.name
                + "\t他的年龄是" + pers.age + "岁\t是否为男性:" + pers.isMale);
    }
}
class Person{
    //属性
    String name;
    int age;
    boolean isMale;
    //方法
    public void eat(){
        System.out.println("他可以吃饭");
    }
    public void sleep(){
        System.out.println("他可以睡觉");
    }
    public void talk(String language){
        System.out.println("他可以使用" + language + "交流");
    }
}

​ 运行结果:

  • 如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static)

    如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。

  • 特别的,如果我们创建一个新的对象pers_t,并将pers的地址赋给pers_t,那么它们就可以共享同一个属性值

Person pers_t = new Person();
pers_t = pers;//将pers变量保存的对象地址值赋给pers_t,导致它们两个指向了堆空间中的同一个对象实体
pers_t.age = 10;
System.out.println(pers.age);

​ 运行结果:

2.对象的内存解析

3.类中属性的使用

  • 属性(也叫做成员变量)与局部变量的异同点:
  1. 相同点:

    ①声明和赋值的格式相同:数据类型 变量名 = 变量值。

    ②先声明,后使用。

    ③都有其对应的作用域。

  2. 不同点:

    ①声明的位置不同

    属性:直接声明在类{}中。

    局部变量:声明在方法中、方法形参、代码块内、构造器形参、构造器内部的变量。

    ②权限修饰符不同

    属性:可以在声明时,指明其权限,使用权限修饰符。

    ​ ---常用的权限修饰符:private、public、缺省、protected。

    局部变量:不可以使用权限修饰符。

    ③默认初始化值不同

    属性:类的属性,根据其类型,都有默认初始化值。

    ---整型0、浮点型0.0、字符型0、布尔型false、引用数据类型null

    局部变量:没有默认初始化值

    ​ 在调用局部变量之前,一定要显式赋值。

    ​ 特别的,形参在调用时赋值,如上方Person类中的language。

    ④在内存中加载的位置不同

    属性:加载到堆空间中(非static)。

    局部变量:加载到栈空间中。

4.类中方法的声明和使用

  • 方法:描述类应该具有的功能。
  • 方法的声明:权限修饰符 返回值类型 方法名(形参列表){方法体}
  1. 权限修饰符:private、public、缺省、protected。

  2. 返回值类型:

    ①有返回值

    如果方法有返回值,则必须在声明时,指定返回值类型。同时,方法中,需要使用return来返灰指定类型的数据,即“return 数据;”。

    ②没有返回值

    如果方法没有返回值,啧在方法声明时,使用void来表示。通常没有返回值的方法中,不使用return,如果使用,仅表示结束此方法,且后面不加数据,即“return;”即可。

​ 3.形参列表:

​ 方法可以声明0个或多个形参

​ 格式:(数据类型1 形参1,数据类型2 形参2,.....,数据类型n 形参n)形参可以为数组

  • return关键字的使用

    1.使用范围:方法体中

    2.作用:

    ①结束方法。

    ②针对有返回值类型的方法,返回所要类型的数据。

  • 方法的使用

    可以调用当前类的属性和方法。

    ---特别的,方法A中又调用了方法A,我们称之为递归方法。

  • 例题1

    定义类Student,包含三个属性:学号int number,年级int state,成绩int score。

    创建20个学生对象,学号为120,年级为(16)和成绩(0~100)都由随机数确定。

    问题一:打印出指定年级学生的信息。

    问题二:使用冒牌排序按学生成绩排序,并遍历所有学生信息。

    代码如下:

    import java.util.Scanner;
    
    
    public class StudentTest {
        public static void main(String[] args) {
            //创建对象
            Student[] stud = new Student[20];
            for(int i = 0;i < stud.length;i++){
                //给数组元素赋值
                stud[i] = new Student();
                stud[i].number = i+1;
                //年级[1-6]
                stud[i].state = (int)(Math.random() * (6 - 1 + 1) + 1);
                //成绩[0-100]
                stud[i].score = (int)(Math.random() * (100 - 0 + 1) + 0);
            }
            //创建Student对象
            Student stude = new Student();
            //因为print()不是static静态方法,所以需要创建一个Studet类对象来调用
            stude.print(stud);
            //调用printState()方法输出指定年级学生的信息
            Student.printState(stud);
            //调用sort()方法对学生成绩进行排序
            Student.sort(stud);
    
        }
    }
    
    class Student{
        int number;//学号
        int state;//年级
        int score;//成绩
        // 遍历输出
        public void print(Student[] stud){
            System.out.println("学号\t年级\t成绩");
            for(int i = 0;i < stud.length;i++){
                System.out.println(stud[i].number + "\t"
                        + stud[i].state + "\t" + stud[i].score);
            }
            System.out.println();
        }
        //输出指定年级学生的成绩
        public static void printState(Student[] stud){
            System.out.println("请输入想输出的年级");
            Scanner scan = new Scanner(System.in);
            int state = scan.nextInt();
            System.out.println(state + "年级学生的成绩如下");
            System.out.println("学号\t年级\t成绩");
            for(int i = 0;i < stud.length;i++){
                if(stud[i].state == state) {
                    System.out.println(stud[i].number + "\t"
                            + stud[i].state + "\t" + stud[i].score);
                }
            }
        }
        //按成绩由低到高使用冒泡排序
        public static void sort(Student[] stud) {
            Student temp = new Student();
            for (int i = 0; i < stud.length - 1; i++) {
                for (int j = 0; j < stud.length - 1 - i; j++) {
                    if (stud[j].score > stud[j + 1].score) {
                        //直接交换对象
                        temp = stud[j];
                        stud[j] = stud[j + 1];
                        stud[j + 1] = temp;
                    }
                }
            }
            System.out.println();
            System.out.println("按成绩从小到大排序:");
            System.out.println("学号\t年级\t成绩");
            for (int i = 0; i < stud.length; i++) {
                System.out.println(stud[i].number + "\t"
                        + stud[i].state + "\t" + stud[i].score);
            }
        }
    }
    

5、匿名对象的使用

①理解:我们创建的对象,没有显式的赋给一个变量名,即为匿名对象

②特征:匿名对象只能调用一次

③使用:代码举例

public class InstanceTest {
    public static void main(String[] args) {
        Phone p = new Phone();
        System.out.println(p);
        p.sendEmail();
        p.playGame();
        //匿名对象
        new Phone().sendEmail();
        new Phone().playGame();
        //两个匿名对象不是同一个对象

        //*********匿名对象的使用************
        PhoneMall mall = new PhoneMall();
        mall.show(new Phone());
    }
}
class PhoneMall{
    public void show(Phone phone){
        phone.sendEmail();
        phone.playGame();
    }
}
class Phone{
    double price;//价格
    public void sendEmail(){
        System.out.println("发送邮件");
    }
    public void playGame(){
        System.out.println("玩游戏");
    }
}

6、方法的重载(overload)

概念:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者类型不同即可。“两同一不同”:同一个类,同一个方法名,不同参数列表

特点:与返回值类型无关,只看参数列表,且参数列表(参数个数、类型)必须不同。调用时,根据方法参数列表的不同来区别。

7、可变个数的形参

JavaSE5.0中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。

①可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载

②可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载,不能共存。例如:public void show(String...strs){}与public void show(String[] strs){}不构成重载。

③可变个数形参在方法的形参中,最多只能声明一个可变个数形参。

④可变个数形参在方法的形参中,必须声明在末尾,例如public void show(String ...strs,int i){}是不符合要求的。

代码举例:

public class MethodArgsTest {
    public static void main(String[] args) {
        MethodArgsTest test = new MethodArgsTest();
        test.show();
        test.show("hallow");
        test.show("world","fun","happy");
    }
    public void show(String s){
        System.out.println("普通形参");
    }
    public void show(String...strs){
        System.out.println("可变个数形参");
    }
    //public void show(string[] strs){}//与可变个数形参不可共存
}

运行结果:

8、方法参数的值传递机制

形参:方法声明时小括号内的参数

实参:方法调用时实际传给形参的参数值

Java的实参值如何传入方法呢?

Java里方法的参数传递方式只有一种:值传递。即将实际参数值的副本(复制品)传入方法内,儿参数本身不受影响。

  • 值传递机制:

    ①形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参。

    ②形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参。

值传递机制练习1_尚硅谷

值传递机制练习2_尚硅谷

9、递归方法

①递归方法:一个方法体内调用它自身

②方法递归包含一种隐式的循环,他会重复执行某段代码,但这种重复无需循环控制。递归一定要向已知方法递归,否则就变成了无穷递归,类似于死循环。

Java面向对象编程(上)(2)

OOP特征之一:封装与隐藏、权限修饰符、构造器、关键字this/package/import

二、面向对象特征之一:封装与隐藏

我们程序设计要求:“高内聚、低耦合”

高内聚:类的内部数据操作细节自己完成,不允许外部干涉;

低耦合:仅对外暴露少量的方法用于使用。

隐藏对象内部的复杂性,值对外公开简单的接口。便于外界调用,从而提高系统的可拓展性、可维护性。通俗地说,把该隐藏的隐藏起来,该暴露的暴露出来,这就是封装性的设计思想。

封装性的体现之一:

①我们将类的属性(xxx)私有化(private),同时提供公共的方法来获取(getXxx)和设置(setXxx)属性的值。

拓展:封装性的其他体现:②不对外暴露的私有方法,③单例模式(将构造器私有化)等等

权限修饰符

①Java4种权限修饰符(从小到大排列):私有(private)、缺省(default)、保护(protected)、公有(public)

②4种权限修饰符可以用来修饰类及类的内部结构:属性、方法、构造器、内部类。

③具体的,4种权限都可以用来修饰类的内部结构,修饰类的话,只能使用缺省和public。

总结封装性

Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小

1.构造器(或构造方法)constructor

①构造器的作用:

  • 创建对象,例:Person p = new Person(); 类名 对象名 = new 此类的构造器;
  • 初始化对象的信息

②定义构造器的格式:权限修饰符 类名(形参列表){}

③一个类中定义的多个构造器,彼此构成重载

说明:如果没有显式的定义类的构造器,系统将默认提供一个空参的构造器,此构造器的权限同类的权限;一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器;也说明,一个类中至少会有一个构造器。

构造器与普通方法的差别:没有返回值类型(void、int等)

总结:属性赋值的先后顺序

①默认初始化(int age;)

②显式初始化(int age = 1;)

③构造器中初始化(Person per = new Person(2);)

④“对象.方法”和"对象.属性"赋值(per.setAge(3)😉

以上操作赋值的先后顺序:①②③④

拓展知识:JavaBean

  • JavaBean是一种Java语言写成的可重用组件。

  • 所谓JavaBean,是指符合如下标准的Java类:

    ①类是公共的

    ②有一个无参的公共的构造器

    ③有属性,且有对应的get、set方法

  • 用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种可以随时随地的复制和粘贴的功能,而不用关心任何变化。

拓展知识:UML类图

例题:

//编写两个类,其中TriAngle类中声明私有的底边长base和高Height,同时声明公共方法访问私有
// 变量此外,提供类必要的构造器。另外一个类中使用这些公共方法,计算三角形的面积。
public class TriAngle {
    private double base;
    private double height;

    public TriAngle(){//空参构造器

    }
    public TriAngle(double b,double h){
        base = b;
        height = h;
    }

    public void setBase(double b){
        base = b;
    }
    public double getBase(){
        return base;
    }
    public void setHeight(double h){
        height = h;
    }
    public double getHeight(){
        return height;
    }
    public double getArea(){
        return (base*height)/2.0;
    }
}
public class TriAngleTest {
    public static void main(String[] args) {

        TriAngle t1 = new TriAngle();//空参构造器
        t1.setBase(3.0);
        t1.setHeight(5.0);
        System.out.println("base = " + t1.getBase() + ",height = "
                + t1.getHeight() + ",Area = " + t1.getArea());
        TriAngle t2 = new TriAngle(3.0,5.0);
        System.out.println("base = " + t2.getBase() + ",height = "
                + t2.getHeight() + ",Area = " + t2.getArea());
    }
}

2.关键字:this的使用

它在方法内部使用,即这个方法所属对象的引用;

它在构造器内部使用,表示该构造器正在初始化的对象。

具体的:我们可以用this来区分局部变量和属性

比如:this.name(属性) = name(形参);

this可以用来修饰、;属性、方法、构造器;

在类的方法中,我们可以使用“this.属性”或“this.方法”的方法,调用当前对象属性或方法;通常情况下,我们都选择省略“this.”,特殊的,如果方法的形参和类的属性同名时,我们必须显式的使用“this.变量”的方式,表明此变量是属性而非形参。

在类的构造器中,我们可以使用“this.属性”或“this.方法”的方法,调用当前正在创建的属性或方法;通常情况下,我们都选择省略“this.”,特殊的,如果方法的形参和类的属性同名时,我们必须显式的使用“this.变量”的方式,表明此变量是属性而非形参。

this调用构造器

①我们在类的构造器中,可以显式的使用“this(形参列表)”方式,调用本类中指定的其他构造器,不能调用自己。

例:

public Person(){
    //this();错误,不能自己调用自己
}
public Person(String name){
    this();//调用无参构造方法
}

②如果一个类中又n个构造器,则最多有n-1构造器中使用了“this(形参列表)”,避免陷入“你调用我,我调用你”的死循环中。

③规定:“this(形参列表)”必须声明在当前构造器的首行,也说明在构造器内部,最多只能声明一个“this(形参列表)",用来调用其他的构造器。

例题Bank:

public class Account {//账户
    private double balance;

    public Account(double balance){
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }

    public void deposit(double amt){//存钱
        if(amt > 0) {
            this.balance += amt;
            System.out.println("存款成功!存入" + amt + "元。");
        }
        else{
            System.out.println("输入有误!");
        }
    }

    public void withdraw(double amt){//取钱
        if(amt <= balance){
            this.balance -= amt;
            System.out.println("取款成功!取出" + amt + "元。");
        }
        else{
            System.out.println("取款失败!余额不足!");
        }

    }
}
public class Customer {//用户
    private String firstName;
    private String lastName;
    private Account account;

    public Customer(String firstName,String lastName){
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }
    
}
public class Bank {//银行
    private Customer[] customers;//存放多个客户
    private  int numberOfCustomers;//记录客户的个数,int型默认0

    public Bank(){
        customers = new Customer[50];
    }

    public void addCustomer(String firstName,String lastName){//添加用户
        Customer customer = new Customer(firstName,lastName);
        customers[numberOfCustomers++] = customer;//调一次,numberOfCustomer自加1
    }

    public Customer getCustomer(int index) {//获取index位置客户
        if(index >= 0 && index < numberOfCustomers){
            return customers[index];
        }
        return null;
    }

    public int getNumberOfCustomer() {//获取客户的个数
        return numberOfCustomers;
    }
}
public class BankTest {//测试类
    public static void main(String[] args) {
        Bank bank = new Bank();

        bank.addCustomer("Jane","Smith");//添加用户Jane·Smith

        bank.getCustomer(0).setAccount(new Account(10000));//创建Jane的银行账户,初始存款10000

        bank.getCustomer(0).getAccount().withdraw(2000);//取钱2000

        bank.getCustomer(0).getAccount().deposit(5000);//存5000
        //打印余额
        System.out.println("客户:" +bank.getCustomer(0).getFirstName() + "账户余额为"
                + bank.getCustomer(0).getAccount().getBalance() + "元。");
        bank.addCustomer("nima","wang");//添加用户wangnima
        System.out.println("银行客户数为:" + bank.getNumberOfCustomer());
    }
}

3.关键字:package、import的使用

package:为了更好的实现项目中类的管理,提供包(package)的概念

​ ①使用package声明类或接口所属的包,声明在源文件的首行

​ ②package属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、”见名知意“

​ ③package名称每出现一个“.”,表明一层文件目录,例:package com.atguigu.javaProject01,表明com目录中atguigu目录下的javaProject01,同一个包下,不能命名同名的接口、类

import:在源文件中显式的使用import结构导入指定包下的类、接口

​ ①声明在包的声明和类的声明之间。

​ ②可以使用“xxx.*"的方式,导入xxx包下的所有结构,但是如果使用的是“xxx”子包下的结构,任然需要显式导入。

​ ③java.lang包是java核心包,程序会自动导入,调用此包的结构(如String/system等)可以省略import。

​ ④调用本包下定义的类或接口,可以省略import。

​ ⑤如果在源文件中,使用了不同的包下同名的类,则必须至少有一个类需要以全类名的方式显示。

​ 例:image-20210321163004641

​ ⑥import static:导入指定类或接口中的静态结构(属性、方法)。

posted @ 2022-01-04 13:46  来杯咖啡  阅读(118)  评论(0)    收藏  举报