美文网首页
day3_面向对象(上)

day3_面向对象(上)

作者: OmewSPG | 来源:发表于2021-12-11 12:42 被阅读0次

    面向对象的三条主要线索:

    1. Java类以及类的成员:属性、方法、构造器、代码块和内部结构
    2. 面向对象的三大特征:封装性,继承性,多态性
    3. 其它关键字:this,super,static,final,abstract,interface,package...

    面向过程与面向对象

    两者区别

    面向对象与面向过程

    思想概述

    思想概述

    Java基本元素:类和对象

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

    • 类是对一类事物的描述,是抽象的、概念上的定义
    • 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。

    类与类的成员

    常见的类的成员:

    • 属性:对应类中的成员变量
    • 行为:对应类中的成员方法
      比如:
    class Person {
        // 属性(变量)
        String name;
        int age;
    
        // 方法(函数)
        public void speak(String lang) {
            System.out.println("Domo, my name is " + this.name + ",I Speak " + lang + "!");
        };
    }
    

    类的语法格式

    语法格式

    对象的创建和使用

    实际上,面向对象的流程:
    创建类 --> 创建类的对象 --> 调用属性或方法

    比如:

    public class OPPtest1 {
        public static void main(String[] args) {
            // 创建person对象 ( 类的实例化 )
            // 创建对象后,属性具有初始化值
            Person p1 = new Person();
            
            // 调用对象结构:属性、方法
            // 调用格式: 对象.属性
            p1.name = "Omew";
            p1.age = 16;
            System.out.println(p1.name);
            p1.speak("Chinese");
            
            // 创建第二个Person对象
            Person p2 = new Person();
            p2.name = "Kana";
            p2.age = 17;
            System.out.println(p2.name);
            p2.speak("Jpanese");
            
            // 将p1变量保存的对象地址赋给p3,导致p1和p3指向了堆空间中的同一个实体
            Person p3 = p1;
            System.out.println(p3.name);  // Omew
            
            p3.age = 10;
            System.out.println(p1.age);   // 10 
        }
    
    内存解析 内存图

    匿名对象

    匿名对象

    比如:

    /*
     * 匿名对象
     * 1.创建匿名对象:没有显式地赋给一个变量名,即为匿名对象
     * 2.特征:一般情况下匿名对象只能调用一次
     * 3.匿名对象作为参数传递给另一个对象的方法时,可以多次调用
     */
    
    public class InstanceTest {
        public static void main(String[] args) {
            new Phone().price = 1999;
            new Phone().showPrice();   // 0.0
            
            //***********************************
            
            PhoneMall mall = new PhoneMall();
            mall.display(new Phone());   // 999.0
        }
    }
    
    
    class PhoneMall{
        public void display(Phone p){
            p.price = 999;
            p.showPrice();
        }
    }
    
    
    class Phone{
        double price;
        
        public void showPrice(){
            System.out.println("该手机的价格为:" + price);
        }
    }
    
    

    上面程序中,每new Phone()一次就新建了一个Phone的实例,因为每个实例的首地址值都是不同的,所以直接对其进行属性赋值和方法调用,可能不会达到你想要的结果;
    一般情况下,匿名对象作为参数放到方法里,才是最好的使用选择

    属性

    语法格式 成员变量(属性)与局部变量

    比如:

    /*
     * 类中的属性
     * 
     * 属性(成员变量)  vs 局部变脸(声明在方法内,方法形参,代码块,构造器内部的变量)
     * 1.声明属性时,可以用权限修饰符进行修饰(private,public,protected,缺省)
     *  .局部变量无法使用权限修饰符
     * 2.类的属性,依据其类型,具有默认初始化值
     *  .局部变量没有默认初始化值,在调用之前一定要显式赋值(形参在调用时赋值即可)
     * 3.类的属性加载在堆空间
     *  .局部变量加载在栈空间
     * 
     */
    public class Type {
        public static void main(String[] args) {
            User u1 = new User();
            System.out.println(u1.name);  // null
            System.out.println(u1.age);   // 0
            System.out.println(u1.isMale);  // false
            
            u1.eat();
            u1.talk("英语");  // 形参在这里赋值
        }
    }
    
    class User{
        // 属性(成员变量)
        String name;
        int age;
        boolean isMale;
        
        public void talk(String lang){  // lang为形参,也是局部变量
            System.out.println("我可以用" + lang + "进行交流");
        }
        
        public void eat(){
            String food = "馅饼";   // food也为局部变量
            System.out.println("北方人喜欢吃: " + food);
        }
        
    }
    
    属性与局部变量的区别 对象属性默认初始化赋值

    方法

    何为方法(method、函数):

    • 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。
    • 将功能封装为方法的目的是,可以实现代码重用,简化代码。
    • Java里的方法不能独立存在,所有的方法必须定义在类里。
    声明格式

    比如:

    package com.atguigu.java;
    /*
     * 类中方法的声明和使用
     * 声明方式:
     *        权限修饰符  + 返回值类型  + 方法名(指定形参 ) {
     *        
     *        }
     *        
     * 权限修饰符: public , private , protected , 缺省
     * 方法名也属于标识符,遵守标识符的命名规则
     * 形参列表:
     *      方法名  (数据类型    形参1 ,  数据类型    形参2...)
     * 
     */
    public class Method {
        public static void main(String[] args) {
            Customer cus1 = new Customer();
            cus1.eat();       // 客户吃饭
            cus1.sleep(8);    // 休息了8个小时
            cus1.name = "Omew";
            System.out.println(cus1.getName());   // Omew
            System.out.println(cus1.getNation("China"));   // 我的国籍是: China
        }
    }
    
    class Customer{
        // 属性
        String name;
        int age;
        boolean isMale;
        
        // 方法
        public void eat(){  // void 无返回值
            System.out.println("客户吃饭");
        }
        
        public void sleep(int hour){
            System.out.println("休息了" + hour + "个小时");
        }
        
        public String getName(){  // 返回String类型数据
            return name;     // 若有返回值,则一定要用 return 关键字
        }
        
        public String getNation(String nation){
            String info = "我的国籍是: " + nation;
            return info;
        }
    }
    

    注意:

    • 方法被调用一次,就会执行一次
    • 没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不必使用return语句。如果使用,仅用来结束方法。
    • 定义方法时,方法的结果应该返回给调用者,交由调用者处理。
    • 方法中只能调用方法或属性,不可以在方法内部定义方法。

    对象数组(例题)

    例题

    可以这样写:

    // 对象数组***
    public class practice3 {
        public static void main(String[] args) {
            
            Student[] stu = new Student[20];   // 创建Student类数组
            for(int i = 0; i < 20; i++){
                stu[i] = new Student();    // 创建Student类的对象
                stu[i].number = i + 1;
                stu[i].state = (int)(Math.random() * 6 + 1);
                stu[i].score = (int)(Math.random() * 101);
            }
            
            
            practice3 pra = new practice3();
            
            pra.printStudent(stu);
            System.out.println("***********************************************");
            
            // 获取三年级学生信息
            
            pra.getStateStudent(stu, 3);
            System.out.println("***********************************************");
            
            // 按成绩冒泡排序
            
            pra.sortByScore(stu);
            pra.printStudent(stu);
            
    
        }
        
        // 封装函数
        // 遍历学生
        public void printStudent(Student[] stu){
            for(int i = 0; i < stu.length; i++){
                stu[i].getStudent();
            }
        }
        
        // 获取该年级学生信息
        public void getStateStudent(Student[] stu, int state){
            for(int i = 0; i < stu.length; i++){
                if(stu[i].state == state){
                    stu[i].getStudent();
                }
            }
        }
        
        // 按成绩排序
        public void sortByScore(Student[] stu){
            Student temp = new Student();   // 注意交换的是整个对象,而不是属性
            for(int i = 0; i < stu.length; i++){
                for(int j = 0; j < stu.length - i - 1; j++){
                    if(stu[j].score > stu[j + 1].score){
                        temp = stu[j];
                        stu[j] = stu[j + 1];
                        stu[j + 1] = temp;
                    }
                }
            }
        }
        
        
    }
    
    
    
    class Student{
        int number;
        int state;
        int score;
        
        public void getStudent(){
            System.out.println("学号: " + number + ",年级" + state + ",成绩" + score);
        }
    }
    
    
    内存解析

    再谈方法

    方法重载

    image.png

    例子:

    package com.atguigu.java;
    
    /*
     * 方法的重载(overload)
     * 同一个类中,允许存在一个以上的同名方法,只需要他们的参数个数和参数类型不同即可
     * 
     * 比如,Arrays类中sort() , binarySearch()
     * 
     * 
     */
    public class methodRebuild {
        public static void main(String[] args) {
            methodRebuild meth = new methodRebuild();
            
            meth.getSum(1,2);    // 3
            meth.getSum(3.13,4.13);    // 7.26
    
        }
        
        
        // 以下两个方法为重载关系
        public void getSum(int i, int j){
            System.out.println(i + j);
        }
        
        public void getSum(double d1, double d2){
            System.out.println(d1 + d2);
        }
        
    }
    
    

    可变形参的方法

    image.png
    image.png

    比如:

    package com.atguigu.java;
    /*
     * 可变个数形参的方法(JDK 5.0)
     * 
     * 
     * 可变形参格式:数据类型 ... 变量名
     * 调用时,传入的参数可以是任意个
     * 可变个数形参的方法,可以和其它同方法名且 不同参数个数的方法构成重载
     * 可变个数形参的方法,不可以和其它同方法名且参数个数为数组的方法构成重载
     * 可变个数形参在方法的形参中,必须声明在末尾,且最多只能声明一个
     * 
     */
    public class Arguments {
        
        public static void main(String[] args) {
            Arguments arg = new Arguments();
            arg.show(2);
            arg.show("Hello");
            arg.show("Hello","World");
        }
        
        public void show(int i){
            System.out.println("show(int i)");
        }
        
        public void show(String s){
            System.out.println("show(String s)");
        }
        
        public void show(String ... strs){
            System.out.println("show(String ... strs)");
        }
        
    //  public void show(String[] strs){
    //      
    //      
    //  }    // 报错;因为这是JDK5.0以前,可变个数形参方法的写法,实际上和上一个函数重名了
        
        
    //  public void show(String ... strs ,int i){
    //      
    //  }   // 报错;可变个数形参在方法的形参中,必须声明在末尾,且最多只能声明一个
        
    }
    
    

    值传递机制(重要)

    image.png

    直接看例子吧:

    package com.atguigu.java;
    /*
     * 方法的形参传递机制:值传递
     * 
     * 1. 形参:方法定义时,声明在小括号内的数据
     *    实参:方法调用时,实际传递给形参的数据
     *    
     * 2. 值传递机制
     * 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值
     * 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值
     * 
     */
    public class methodArguments {
        public static void main(String[] args) {
            //************************基本数据类型*******************************
            int m = 10;
            int n = 20;
            System.out.println("m: "+ m + ",n: " + n);
            
            methodArguments test = new methodArguments();   // m: 10,n: 20
            test.exchange(m, n);
            System.out.println("m: "+ m + ",n: " + n);   // m: 10,n: 20
            
            
            //************************引用数据类型*******************************
            numbers nums = new numbers();
            nums.m = 10;
            nums.n = 20;
            System.out.println("nums.m: "+ nums.m + ",nums.n: " + nums.n);   // nums.m: 10,nums.n: 20
            
            test.exchange(nums);
            System.out.println("nums.m: "+ nums.m + ",nums.n: " + nums.n);   // nums.m: 20,nums.n: 10
            
        }
        
        
        public void exchange(int m, int n){
            int temp = m;
            m = n;
            n = temp;
        }
        
        public void exchange(numbers nums){   // 方法重载
            int temp = nums.m;
            nums.m = nums.n;
            nums.n = temp;
        }
        
        
    
    }
    
    class numbers{
        int m,n;
    }
    
    
    

    可以看到,如果传入的是普通数据类型,实际上并没有发生值的交换(即传入的是副本,仅仅交换副本并不会影响原始数据);
    但是如果传入的是引用数据类型,因为实际上传入引用数据类型都是地址值,所以形参和实参会同时指向这个引用数据类型(比如说堆空间中对象),修改形参就是修改实参

    总之记住:

    • 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值
    • 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值

    这是绝对不会错的

    一道很综合的例题

    例题

    我是这样写的:

    package com.atguigu.practice;
    /* 1.定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个
         findArea()方法返回圆的面积。
    
     * 2.定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义
                   如下:public void printAreas(Circle c, int time)
           在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。
           例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。
           
     * 
     */
    public class practice1 {
        public static void main(String[] args) {
            Circle c = new Circle();
            PassObject test = new PassObject();
            
            test.printArea(c, 5);
            System.out.println("now radius is: " + c.radius);
        }
    }
    
    class Circle{
        double radius;
        
        public double findArea(){
            return Math.PI * radius *radius;
        }
    }
    
    class PassObject{
        public void printArea(Circle c ,int time){
            System.out.println("Radius" + "\t" + "Area");
            for(int i = 1; i <= time; i++){
                c.radius = i;
                System.out.println(c.radius + "\t" + c.findArea());
            }
            c.radius ++;
        }
    }
    
    

    递归

    image.png

    注:什么叫做往已知方向递归?
    比如上面那个求1-100的和,已知情况是当 num = 1 的时候 sum = 1 ;所以在求
    当 num = 100 时 sum(100) 的值,会先求的 sum(num - 1) 、即sum( 99 ) 的值;如果想求 sum(99) 的值,就要先求sum(98) 的值... 以此类推,朝着已知条件sum(1) = 1 这个方向前进,这就是朝已知方向递归。

    典型例子:
    已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值。

        public int getSeq(int n){
            if(n == 0){
                return 1;
            }else if(n == 1){
                return 4;
            }else{
                return 2 * getSeq(n - 1) + getSeq(n - 2); 
            }
        }
    

    朝已知条件方向f(0) = 1, f(1) = 4前进,只能是用减法推回去
    但是如果:
    已知一个数列:f(20) = 1,f(21) = 4,f(n+2) = 2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值。

        public int getSeq1(int n){
            if(n == 20){
                return 1;
            }else if(n == 21){
                return 4;
            }else if(n < 20){
                return getSeq1(n + 2) - 2 * getSeq(n + 1); 
            }else{    // 如果当 n > 20 毫无疑问只能向已知条件的反方向递归了
                return 2 * getSeq1(n - 1) + getSeq1(n - 2); 
            } 
        }
    

    顺便一提,如果这里试求f(30),那么和上一题f(10)的结果是一样的,都是10497

    斐波那契数列:

    image.png
        public int Fibonacci(int n){
            if(n == 1){
                return 1;
            }else if(n == 2){
                return 1;
            }else{
                return Fibonacci(n - 1) + Fibonacci(n - 2);
            }
        }
    

    相关文章

      网友评论

          本文标题:day3_面向对象(上)

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