Java阶段二——面向对象OOP(基础)

一、概述和变量 

 面向过程 VS 面向对象:

  • 面向过程:以‘函数’为组织单位;是一种执行者思维,适合解决简单的问题。扩展能力差、后期维护难度较大。
  • 面向对象: 为组织单位。每种事物都具备自己的 属性行为/功能 。是一种“ 设计者思维 ”,适合解决复杂问题。代码扩展性强、可维护性高。

        理解来说,面向过程:如何开车?按步骤一步步实现就行,点火---踩离合---踩油门---行进。 

        面向对象:如何造车?需要多功能的协作。需要从宏观上去考虑。

         面向对象离不开面向过程。面向对象的实现其中也包含面向过程的函数。

类(class)和对象 (object):

  • 类:具有相同特征的事物的抽象描述,是 抽象的 、概念上的定义。例如:人。  
  • 对象:实际存在的该类事物的 每个个体 ,是 具体的 ,因而也称为 实例(instance) 。例如:比尔盖茨等。

 类的成员:

  • 属性:也是成员变量==属性==field(字段、域)。
  • 行为:成员方法==函数==method。

面向对象实现具体功能的三个步骤(重要) :

  1. 创建类,并设计类的内部成员(属性+方法) 。例如 class Phone。
  2. 创建对象(类的实例化),例如Phone phone=new Phone()。一般是在其它非Phone类中new一个。
  3. 通过对象,调用其属性和方法。

Java中内存结构的划分

  • java中内存结构划分为:虚拟机栈、堆、方法区、程序计数器、本地方法栈 。
  • 虚拟机栈:以栈帧为基本单位,有出栈和入栈操作。每个栈帧入栈出栈操作对应一个方法的执行。方法内的局部变量存储在栈帧中。
  • 堆空间:new出来的结构(数组、对象)。1、数组:数组的元素在堆中;2、对象的成员变量在堆中
  • 方法区:加载的类的模板结构。

类的内存解析: 

  • 堆:存放new出来的对象,对象中存放的是对象的成员变量(起初是默认值)。没有new,就不会新开辟空间。
  • 栈:存放的是对象名,代表的是对象的首地址。调用成员变量时,会给对中的成员变量赋值。
  • 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的
    代码等数据。

 成员变量 VS 局部变量:

  •  成员变量:1、类内-方法体外声明的变量。2、随对象的创建,存储在空间。3、随着对象的创建而创建,随着对象的消亡而消亡。4、能用权限修饰符修饰。5、有默认值,所以不用必须显式赋值。
  • 局部变量:1、方法体内等位置声明的变量。2、存储在空间。3、随着方法对应的栈针入栈,局部变量在栈中分配,随着方法对应的栈针出栈,局部变量消亡。4、不能用任何权限修饰符进行修饰。5、在使用局部变量前,必须显式赋值。

  • 任何一个类都可以作为另一个类中属性的类型。作为引用数据类型。

        例如,员工的生日属性。首先,需要先创建Mydate类,包含了生日的年、月、日属性。

        接着,在另一个类中声明birthday时,可以直接使用Mydate类作为生日的数据类型。 

        接着,创建对象,调用属性。

二、方法 

方法的声明 

  •  public void eat();
  • public void sleep(int hour);
  • public String interests(String hobby);   //调用该方法时要传入形参,并有一个变量接收返回值
  • public int age();   //调用该方法时,要有一个变量接收返回值。

        在声明方法时,要不要提供返回值类型(要不要有返回值)以及要不要传形参,则具体问题具体分析。

方法注意事项

  • 必须先声明后使用,且方法必须定义在类的内部
  • 调用一次就执行一次,不调用不执行。
  • 方法中可以调用类中的方法或属性,不可以在方法内部定义方法。

return关键字的作用 

  • 作用 1 :结束一个方法。
  • 作用2 :结束一个方法的同时,可以返回数据给方法的调用者。
  • return后面不能再声明执行语句。

Q:如果main和方法在同一个类中,在main中不声明对象调用方法的情况下,方法要用static修饰???(后面待解决)

例如,

        这样的话,方法会报错。当方法声明时加上static后,可以不用创建对象调用。例如:

 方法调用的内存解析

        每一个方法对应一个栈针。

  • 方法 没有被调用 的时候,都在 方法区 中的字节码文件(.class)中存储。
  • 方法 被调用 的时候,需要进入到 栈内存 中运行。方法每调用一次就会在栈中有一个 入栈 动作,即
  • 给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值。
  • 当方法执行结束后,会释放该内存,称为 出栈 ,如果方法有返回值,就会把结果返回调用处(例如在main方法中调用某个方法,该方法的返回值是返回到main中),如果没有返回值,就直接结束,回到调用处继续执行下一条指令。
  • 栈结构:先进后出,后进先出。

 三、对象数组

        数组的元素可以是基本数据类型,也可以是引用数据类型。当 元素是引用类型中的类 时,我们称为对象数组。

         例如,我们要查看20个学生对象的基本信息,其中,每个学生对象都包含多个属性,“学生”就可作为一个类作为引用数据类型。20个学生组成对象数组,其中每个元素即每个学生的数据类型是“学生”。

        格式举例:Student[ ] students=new Student[20];

        当我们需要为每个学生对象的属性赋值等操作时,需要再次创建对象。即students[i]=new Student()。 

案例一:

定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。 创建20个学生对象,
* 学号为1到20,年级和成绩都由随机数确定。
* 问题一:打印出3年级(state值为3)的学生信息。
* 问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息

public class student_Test {
    public static void main(String[] args) {
        Student[] students=new Student[20];//创建对象数组。
        for(int i=0;i<students.length;i++){
            students[i]=new Student();//要为20个学生依次new对象,堆中
            students[i].number=i+1;
            students[i].state=(int)(Math.random()*6+1);//[1,6]
            students[i].score=(int)(Math.random()*101);//[0,100]
        }
        int state=3;
        for(int i=0;i<students.length;i++){
            if(state==students[i].state){
                System.out.println(students[i].printStudents());
            }
        }
        //局部变量存放在栈中。属性(成员变量)在堆中
        for(int i=0;i<students.length-1;i++){
            boolean flag=true;//元素已经是排好序的
            for(int j=0;j<students.length-1-i;j++){
                if(students[j].score>students[j+1].score){
                    Student temp=students[j];//这里使用students[j].score是错误的,因为我们要把学生的所用属性进行一个交换。
                    students[j]=students[j+1];
                    students[j+1]=temp;
                    flag=false;//设为false,证明元素在该轮交换了一次
                }
            }
            if(flag){ //如果为true,则表明在i轮后,并未再次进行交换,元素已经有序,此时可以跳出循环了
                break;
            }
        }
        System.out.println();
        for(int i=0;i<students.length;i++){
            System.out.println(students[i].printStudents());
        }

    }
}

案例二:

1 )定义矩形类 Rectangle ,包含长、宽属性, area() 返回矩形面积的方法, perimeter() 返回矩形周长的
方法, String getInfo() 返回圆对象的详细信息(如:长、宽、面积、周长等数据)的方法
2 )在测试类中创建长度为 3 Rectangle[] 数组,用来装 3 个矩形对象,并给 3 个矩形对象的长分别赋值
10,20,30 ,宽分别赋值为 5,15,25 ,遍历输出

        案例二 内存解析:

        area()方法和perimeter()方法调用都将返回值返回到getInfo()中, 然后依次出栈。

四、方法的重载 

定义 

        在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。【参数列表不同,意味着参数个数或参数类型的不同 ,和形参名、修饰符、返回值类型都没有关系。】 

        Q:编译器是如何确定调用的是某个具体的方法呢? 

        答:编译器先通过方法名确定一波重载的方法,再根据形参列表确定具体的某一方法。 

注意:下图中,打印出的值为什么中间这个是具体的值,其它是地址值?

         因为println是有一系列重载的方法,如下图,其中这个数组arr1和arr2(引用类型了)就对应Object。Object类型默认是打印地址值。

 五、可变个数的形参

        格式: 方法名(参数类型... 参数名)

  • 可变个数形参的方法在调用时,可赋的实参个数可以是0个、1个或多个。

  • 可变个数形参的方法与同名的方法之间,彼此构成重载 。 

  • 可变参数方法的使用与方法参数部分使用数组是一致的,二者不能同时声明,否则报错。 

  • 方法的参数部分有可变形参,需要放在形参声明的最后. 

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

 场景举例:

六、方法的值传递机制(重要)

 1、(复习)对于方法内声明的局部变量来说:

        如果出现赋值操作,

  • 如果是基本数据类型的局部变量,则把数据值传递过去。
  • 如果是引用数据类型(有数组或对象)的局部变量,则把其地址值传递过去。

2、方法的参数传递机制

  • 如果形参是基本数据类型的变量,则将实参保存的数据值传递给形参。
  • 如果形参是引用数据类型的变量,则将实参保存的地址值传递给形参。

3、内存解析: 

Java面试题: JAVA中的参数(变量)传递机制是什么?答:值传递。看传的是什么值了。(不是引用传递。)

 答案:

//方法一:
public void method(int a,int b){
        a=a*10;
        b=b*20;
        System.out.println("a="+a);
        System.out.println("b="+b);
        System.exit(0);//正常终止当前运行的Java虚拟机,main函数后面的代码则不会再运行了。
    }

//方法二:暂时先不理解了,等后续再看!!

public static void method(int a, int b) {
    PrintStream ps = new PrintStream(System.out) {
    @Override
    public void println(String x) {
        if ("a=10".equals(x)) {
        x = "a=100";
      } else if ("b=10".equals(x)) {
        x = "b=200";
        }
        super.println(x);
        }
        };
    System.setOut(ps);
}

 七、方法的递归

举例:

注意:

        1. 递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环 慢的
多 ,所以在使用递归时要慎重。
        2. 在要求高性能的情况下尽量避免使用递归,递归调用既花时间又 耗内存 。考虑使用循环迭
代。   

八、面向对象的特征一:封装性      

        内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度 的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。 

 1、为什么要封装?

理论上:

  • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
  • 低耦合:仅暴露少量的方法给外部使用,尽量方便外部调用。

通俗来讲,就是把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。 

2、如何实现数据封装?

  • 使用4种权限修饰符public protected 缺省 private

         其覆盖范围如下:(这里的子类估计就是继承那种的子类)

  •  具体修饰结构
    •  外部类:public缺省
    • 成员变量、成员方法、构造器、成员内部类:publicprotected缺省private        

3、封装作用

        可以使用4种权限修饰符修饰类及类的内部成员,当这些成员被调用时,体现其可见性的大小。【问什么是封装性呢?】

4、案例 

public class AnimalTest {
    public static void main(String[] args) {
        Animal animal=new Animal();

        animal.name="小兔子";
        //animal.legs=4;
        //animal.legs=-4;//数据非法,此时如果在这里直接赋值是不可以的。但在Animal类中不能直接单纯输入赋值的执行语句。所以要写入set/get方法。

        //调用该方法对Animal类中的legs属性赋值,并将Animal类中legs属性私有化(private)
        animal.setLegs(4);
        animal.getLegs();//获得Animal类中legs的值
    }

}

class Animal{
    String name;

    //因为legs私有化了,所以legs出了Animal类外就不能被调用了。
    private int legs;

    public void setLegs(int leg){
        if(leg>0&&leg%2==0){
            legs=leg;
        }else
            System.out.println("您输入的数据非法");
    }
    public int getLegs(){
        return legs;
    }
}

        在题目中,我们给Animal的对象的legs属性赋值。在实际的常识中,我们知道legs不能赋值为负数的。但是如果直接调用属性legs,是不能加入判断逻辑的。那怎么办呢? 步骤如下:

  1. 将legs属性私有化(private),禁止在Animal类的外部直接调用此属性。
  2. 提供给legs属性赋值的setLegs()方法,在此方法中加入legs赋值的判断逻辑if(legs>=0&& legs % 2 ==0)将此方法暴露出去,使得在Animal类的外部调用此方法,对legs属性赋值。
  3. 提供给legs属性获取的getLegs()方法,此方法对外暴露。是得在Animal类的外部还可以调用此属性的值。

九、构造器(constructor)

 1、作用

  • 搭配new关键字,创建类的对象。 
  • 在创建对象的同时,能够给对象的相关属性赋值。

2、使用说明 

  • 声明格式:权限修饰符 类名(形参列表){ } 
  • 创建类以后,在没有显示提供任何构造器的情况下,系统会默认提供一个空参的构造器,且构造器的权限和类的权限相同。如下: 
public class Person {
    private String name;
    private int age;
    
    //默认有的无参构造器(这里显式的展示出来了)
    public Person(){ //对于public,如果类名前面没有public,则构造器也不写

    }
    //方法
    public void eat(){
        System.out.println("吃饭");
    }
}
public class PersonTest {
    public static void main(String[] args) {
        Person p1=new Person();//其中这个Person()就是一个构造器,对应Person类中显式展现出来的那一句

    }
}
  • 一旦类中声明了带参的构造器,则系统不再提供默认的那个无参构造器了。 
  • 一旦类中声明了多个构造器,则彼此之间构成重载。例如:

3、对象数组调用构造函数

        具体使用举例:

public class Student {
    private String name;
    private int age;
    private String school;
    private String major;
    //设置三个有参构造器
    public Student(String n, int a){
        name=n;
        age=a;
    }
    public Student(String n, int a, String s){
        name=n;
        age=a;
        school=s;
    }
    public Student(String n, int a, String s, String m){
        name=n;
        age=a;
        school=s;
        major=m;
    }

    public String printStudents(){
        return name+"的年龄为"+age+",学校是"+school+",专业是"+major;
    }
}
public class StudentTest {
    public static void main(String[] args) {
        Student[] students=new Student[2];
        Scanner scanner=new Scanner(System.in);
        String name;
        int age;
        for(int i=0;i<students.length;i++){
            System.out.print("请输入姓名:");
            name=scanner.next();
            System.out.print("请输入年龄:");
            age=scanner.nextInt();
            //Student temp=new student(name,age); students[i]=temp; 也正确
            students[i]=new Student(name,age);//这里可以根据构造器设置的参数数量进行赋值。
        }

        for(int i=0;i<students.length;i++){
            System.out.println(students[i].printStudents());
        }
    }
}

4、具体案例练习 

        案例  账户客户  练习可见Github,目录文件地址:chapter06_oop/src/Exercise06/Test03中.  【点下面那个地址进入github后,需要把’main‘换成’master‘,为啥这末麻烦?我git bash的时候哪句语句需要改一下?有大佬知道吗??呜呜呜~

        Github地址:https://github.com/201917421/JavaSECode.git

        Java上传到Github具体步骤参考博主教程: https://mbd.baidu.com/ma/s/cwbNUMeu

http://【如何将项目上传到github(自用)】https://mbd.baidu.com/ma/s/uXgm4JqH

十、JavaBean理解

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

  • 类是公共的
  • 有一个无参的公共的构造器
  • 有属性,且有对应的getset方法 

        使用快捷键alt+insert可以直接生成get、set方法。 

十一、UML类图

小CASE 

Idea安装插件可查看字节码文件:博主链接:https://baijiahao.baidu.com/s?id=1774217601013215141&wfr=spider&for=pc 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值