Java基础:面向对象编程(基础)

一、使用对象的原因

        如果我们需要对猫进行一个描述(名字、年龄、走路、喵叫......)

        ①单独用变量来记录数据,把信息拆解了,不利于数据的管理;

        ②用数组来记录数据,会出现如下问题:

        1)数据类型体现不出来;

        2)只能用下标才能获取对应信息,导致变量名字、内容的对应关系不明确;

        3)不能体现我们对象的行为。

        一个程序就是一个世界,有很多事务(对象(属性、行为))

二、类与对象

 2.1 类与对象的关系

        1、类是抽象的,概念的,代表一类事物,比如人类、狗类...,即它是一种数据类型

        2、对象是具体的 ,实际的,代表一个具体的事务,比如一只具体的猫,即它是一个实例

        3、类是对象的模板,而对象是类的一个个体,对应一个实例。

2.2 对象在内存中的存在形式

        对象名(对象引用)只是用来指向对象的,真正的对象是对象空间(内存中存储的地址和数据)。

public class Object01 {
    public static void main(String[] args) {
        Cat cat01 = new Cat();
        cat01.age = 12;
        cat01.color = "白色";
        cat01.name = "小白";

    }
}
class Cat{
    String name;
    String color;
    int age;
}

 2.3 属性与成员变量

        2.3.1 两者关系

        1、属性与成员变量是同一个意思。(这也有种叫法:字段(field)

        2、属性是类的一个组成部分,一般是基本数据类型,也可以是引用数据类型(对象,数组)。

        2.3.2 属性的注意事项

        1、属性的定义语法类似变量: 访问修饰符  属性类型  属性名

        2、有四种访问修饰符:public,protected,默认,private;

        3、访问修饰符的作用:控制属性或方法的访问范围

        4、如果属性不赋值,会有默认值,规则和数组一样;

2.4 类与对象的内层分配机制

        Java内存结构分析

        1、栈:一般存放基本数据类型(局部变量);

        2、堆:存放对象(Cat cat,数组等);

        3、方法区:常量池(常量,例如字符串),加载类信息;

2.5 成员方法(简称方法)

        在某些情况下,我们需要定义成员方法。比如人类,除了基本的属性外(年龄,姓名...),我们人类有会有一些动作方法:说话、跑步...

        2.5.1 方法调用小结

        1、当程序执行到方法时,就会开辟一个独立的空间(栈空间);

        2、当方法执行完毕后,或者执行到return语句时,就会返回;

        3、返回到调用方法的地方(返回后,原本的开辟的栈就会销毁);

        4、返回后,继续执行剩下的程序。

        2.5.2 方法的作用(好处)

        考虑这么一个情况,如果你的程序中有多个for循环输出,那你是不是要一遍一遍重写,而且当需要对输出部分的代码进行修改时,是不是还得一个一个重新改,这样子太麻烦了。我们是不是可以写一个for循环输出的方法,任何需要输出时调用改方法即可,而且要对输出部分代码进行修改时,只需要修改方法,就可以达到各个输出的部分全部修改。

        1、提高代码复用率,降低代码冗余;

        2、将实现的细节封装起来,然后提供其他用户来调用即可;

        2.5.3 方法的定义

        访问修饰符  返回数据类型   方法名(形参列表...){ //方法体

                代码块;

                return  返回值; //可有可没有。

        }

        例子:

class Person{
    String name;
    int age;

    //方法(成员方法)
    //
    //1.public: 表示方法是公开的;
    //2.void: 表示方法没有返回值;
    //3.speak(): speak是方法名,()中是形参列表;
    public void speak(){
        System.out.println("我是个好人");
    }
}

        2.5.4 方法细节

①访问修饰符:

        1、有四种访问修饰符:public,protected,默认,private;

        2、访问修饰符的作用:控制属性或方法的访问范围

②返回数据类型:

        1、一个方法最多只有一个返回值;(需要返回多个结果可以返回数组)

        2、返回类型可以为任意类型,包含基本类型和引用类型(数组、对象)

        3、如果方法要求有返回数据类型,则方法体中最后执行的语句必须为 return 值;

        (要求返回值类型必须和return的值一致或是兼容(就是可以自动类型转换))

        4、如果方法的返回数据类型为void,方法体中可以没有return语句,或则只写 return。

③方法命名:

        1、命名遵循小驼峰命名法

        2、方法名要见名知意!

④形参列表:

        1、一个方法可以没有形参或则多个形参;

        2、形参类型可以为任意类型,包含基本类型或引用类型;

        3、实参和形参的类型要一致或兼容、个数、顺序必须一致

⑤方法体:

        1、方法不可嵌套定义

⑥方法调用:

        1、同一个类中的方法可以直接调用

        2、跨类调用方法的话,需要通过对象调用方法

        3、跨类调用方法时,还和访问修饰符相关;

⑦方法传参机制:

        1、对于基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参

        (如果需要取得基本数据类型的地址可以将其转化为包装类)

        2、对于引用数据类型传递的是地址(值是地址),可以通过形参影响实参

三、方法的递归调用

        方法的递归,就是方法调用自己。

3.1 递归解决的问题

        1、各种数学问题:8皇后问题,汉诺塔,乘阶问题,迷宫问题,球和篮子问题;

        2、各种算法:二分查找,快排,归并排序,二分查找,分治算法等;

        3、用栈来解决问题,递归代码简洁。

3.2 递归的规则

        1、执行一个方法时,就创建一个新的受保护的独立空间(栈空间);

        2、方法的局部变量的独立的,不会相互影响;

        3、如果方法中使用的是引用数据类型变量(比如数组,对象),就会共享该引用类型的数据。

        4、递归必须向退出递归的条件推进,否则会无限递归,出现StackOverflowError;

        5、当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁;同时,当方法执行完毕或者返回时,该方法也就执行完毕。

3.3 例题

        3.3.1 迷宫问题

public class Labyrinth {
    public static void main(String[] args) {

        //思路
        //1.先创建迷宫,用二维数组表示 int[][] map = new int[8][7];
        int[][] map = LabyrinthTools.mapInit();
        //2.输出迷宫(测试)
        LabyrinthTools.outputOfMap(map);
        //3.开始找路
        LabyrinthTools.findway(map,1,1);
        //4.输出迷宫(测试)
        System.out.println("找到的路为:");
        LabyrinthTools.outputOfMap(map);


    }
}

class LabyrinthTools{
    public static int[][] mapInit(){ //地图初始化
        //思路
        //1.先创建迷宫,用二维数组表示 int[][] map = new int[8][7];
        //2.规定:0表示可走,1表示障碍物

        int[][] map = new int[8][7];
        //3. 将四周全部设置为1
        for(int i = 0; i < 7; i++){
            map[0][i] = 1;
            map[7][i] = 1;
        }
        for(int i = 0; i < 8; i++){
            map[i][0] = 1;
            map[i][6] = 1;
        }
        map[3][1] = 1;
        map[3][2] = 1;

        return map;
    }
    public static void outputOfMap(int[][] arr){ //输出地图
        for(int i = 0; i < arr.length; i++){
            System.out.println();
            for(int j = 0; j < arr[i].length; j++){
                System.out.print(arr[i][j]+" ");
            }
        }
        System.out.println();
    }

    public static boolean findway(int[][] map, int i , int j){
        //1. findway专门来找出迷宫的路径
        //2. 如果找到,就返回true,否则就返回false
        //3. map就是二维数组,即表示迷宫
        //4. i和j表示老鼠当前的位置
        //5. 入口为(1,1),出口为(6,5)
        //6. 因为是递归找路,所以我们先规定map数组中各个值的含义:
        //      0 表示可以走,且还没走过, 1 表示障碍物, 2 表示可以走, 3 表示走过了,但是走不通
        //7. 当出口(6,5)的值2时,表示我们走通了
        //8. 找路策略:先看下,后看右,再看上,最后看左
        if(map[6][5] == 2){
            return true;
        }else{
            if(map[i][j] == 0){//该位置为0,表示还没走,就走走看
                //我们假定可以走通
                map[i][j] = 2;
                //开始找路策略:
                if(findway(map,i+1,j)){
                    return true;
                }else if(findway(map,i,j+1)){
                    return true;
                }else if(findway(map,i-1,j)){
                    return true;
                }else if(findway(map,i,j-1)){
                    return true;
                }else{ //都走不通
                    map[i][j] = 3;
                    return false;
                }

            } else{//走过的就不要再走了
                return false;
            }
        }
    }
}

        输出结果:(2表示通路)

        3.3.2 汉诺塔 

public class HanoiTower {
    public static void main(String[] args) {
        int num = 3;
        char tower01 = 'A';
        char tower02 = 'B';
        char tower03 = 'C';
        HanoiTowerTools.move(num,tower01,tower02,tower03);

    }
}

class HanoiTowerTools{
    public static void move(int num,char a,char b,char c){
        //num表示要移动的个数,a、b、c表示A塔、B塔和C塔
        //即A表示盘的出发点,B表示借助的塔,C表示目的地
        //如果只有一个盘 num = 1
        if(num == 1){
            System.out.println(a + "->" + c);
        }else{
            //如果有多个盘,可以看成两个,即最下面的和上面的所有盘(num-1)
            move(num-1,a,c,b);
            //把最下面的盘移动到c
            System.out.println(a + "->" + c);
            //把b中的所有盘移动到c,借助a盘
            move(num-1,b,a,c);
        }
    }

}

        输出结果: 

        3.3.3 八皇后问题

public class EightQueens {
    public static void main(String[] args) {
        //初始化棋盘
        int[][] checkerboard = EightQueensTools.checkerboardInit();
        //八皇后摆放
        EightQueensTools eigthQueensPlay = new EightQueensTools();
        eigthQueensPlay.play(checkerboard,0);
        System.out.println("总共有" + eigthQueensPlay.count + "个解");
    }
}

class  EightQueensTools{
    int count = 0; //用于解法的计数
    public static int[][] checkerboardInit(){ //棋盘初始化
        int[][] checkerboard = new int[8][8];
        return checkerboard;
    }

    public static void checkerboardPrint(int[][] checkboard){ //棋盘打印
        for(int i = 0; i < checkboard.length; i++){
            for(int j = 0; j < checkboard[i].length; j++){
                System.out.print(checkboard[i][j] + " ");
            }
            System.out.println();
        }
    }

    public void putQueenPlay(int[][] arr, int row){//八皇后摆放
        for(int col = 0; col < 8; col++){//遍历本行的8种摆放情况
            if(ableToPut(arr,row,col)){ //可以摆放
                //该位置可以摆放,置1
                arr[row][col] = 1;
                //第八列也置1好了,得出解
                if(row == 7){ //递归出口
                    System.out.println("找到一个解!");
                    checkerboardPrint(arr);
                    count++;
                }else{
                    putQueenPlay(arr,row+1);
                }
            }
            arr[row][col] = 0;

        }
    }

    public void play(int[][] arr, int row){ //主要为了防止多次调用putQueenPlay方法而倒数count不准确
        this.count = 0;
        putQueenPlay(arr,row);
    }

    public static boolean ableToPut(int[][] arr, int row, int col){ //判断该位置是否能摆放皇后
        //判断同行同列的有没有皇后
        for(int i = 0; i < 8; i++){
            if(arr[row][i] == 1){//同行有皇后
                return false;
            }else if(arr[i][col] == 1){//同列有皇后
                return false;
            }
        }
        //左上有皇后
        for(int i = row, j = col;i >= 0 && j >= 0;i--,j--){
            if(arr[i][j] == 1){
                return false;
            }
        }
        //左下有皇后(可省略)
        for(int i = row, j = col;i < 8 && j >= 0;i++,j--){
            if(arr[i][j] == 1){
                return false;
            }
        }
        //右上有皇后
        for(int i = row, j = col;i >= 0 && j < 8;i--,j++){
            if(arr[i][j] == 1){
                return false;
            }
        }
        //右下有皇后(可省略)
        for(int i = row, j = col;i < 8 && j <8;i++,j++){
            if(arr[i][j] == 1){
                return false;
            }
        }
        //都没有,可以摆放
        return true;
    }
}

         运行结果:

......

 四、方法重载(OverLoad)

4.1 基本介绍

        Java中允许同一个类中,多个同名的方法存在,但形参列表要不一样

        (形参列表不同是重载,一样就是同名了!)

public class OverLoad01 {
    public static void main(String[] args) {
        System.out.println(OverLoadTools.calculate(1,2));
        System.out.println(OverLoadTools.calculate(1.3,2.1));
    }

}
class OverLoadTools{

    public static int calculate(int n1, int n2){
        System.out.println("调用了方法1");
        return n1 + n2;
    }

    public static double calculate(double n1, double n2){
        System.out.println("调用了方法2");
        return n1 + n2;
    }
}

4.2 重载的好处

        1、减轻了起名的麻烦;

        2、利于我们的接口编程。

4.3 方法重载的细节

        1、方法名必须相同;

        2、形参列表必须不同(形参的类型个数顺序必须有一个不一样);

        3、返回类型:无要求(即使方法返回类型不同,但形参的类型个数顺序一样,就会出错)。

五、可变参数

5.1 基本介绍

        Java 允许将同一个类中多个同名同功能参数个数不同的方法,封装成一个方法。这样就可以通过可变参数的实现。

5.2 基本语法

        访问修饰符  返回类型  方法名 (数据类型... 形参名) { // ... 是固定的,要这样写

        }

public class VarParameter {
    public static void main(String[] args) {
        System.out.println(VarParameterTools.calculate(10,11,13,12));
    }
}

class VarParameterTools{

    public static int calculate(int... n){ // 可以计算2个数、3个数、4、5...个数的和
        // 1. int... 表示接受的是可变参数,类型是int,可接受多个int(0 - 多个)
        // 2. 使用可变参数时,可以当作数组来使用,即 n 可当作数组
        int sum = 0;
        for(int i = 0; i < n.length; i++){
            sum += n[i];
        }
        return sum;
    }
}

5.3 可变参数的细节

        1、可变参数的实参可以是0个多个

        2、可变参数的实参也可以是一个数组

        3、可变参数可以和普通参数一起放在形参列表,但是可变参数一定要放最后

        4、一个形参列表中,最多只能有一个可变参数

六、作用域

6.1 基本介绍

        在面向对象中,变量作用域是非常主要的知识点。

        1、在Java编程中,主要的变量就是属性(成员变量)局部变量

        2、我们说的局部变量一般是指在成员方法中定义的变量;

        3、Java中作用域的分类:全局变量和局部变量;

        ① 全局变量:也就是属性,作用域为整个类体;

        ② 局部变量:除了属性之外的其他变量,作用域为定义它的代码块。

         4、全局变量可以不赋值,直接使用,因为有默认值;但是局部变量必须赋值后才能使用,因为没有默认值。

6.2 作用域细节(5点)

        1、属性和局部变量可以重名,访问时遵循就近原则

        2、在同一个作用域中,两个变量不可重名;

        3、属性的生命周期较长,伴随着对象的创建也创建,伴随着对象的死亡而死亡。局部变量的生命周期较短,伴随着它的代码块的执行而建立,伴随着代码块的结束而死亡。

        4、作用范围的不同:

        ① 全局变量可以被本类使用,或其他类使用(通过对象调用);

        ② 局部变量只能在本类的对应方法中使用。

        (如下图,main方法中可以调用OverLoadTools这个对象的属性name,但调用不了在OverLoadTools中定义的局部变量n3。)

        5、修饰符的不同:

        ① 全局变量/属性可添加修饰符;

        ② 但局部变量不可以添加修饰符。

        (如下图,name属性可以添加修饰符,而局部变量n3添加不了修饰符)

七、构造方法 / 构造器

        之前我们在创建对象时,是先new一个对象,然后再给对象的属性赋值;如果我们现在要在创建对象时,就指定这个对象的属性,就需要使用到构造器。

7.1 基本介绍

        构造方法又称为构造器(constructor),是类的一种特殊方法,主要作用是完成对新对象的初始化。(即构造器不是取创建对象,而只是对对象进行初始化!

        它拥有如下特点:

        ① 方法名和类名相同;

        ② 没有返回值;

        ③ 在创建新对象时,系统会自动调用该类的构造器来完成对象的初始化。

7.2 基本语法

         修饰符  方法名 (形参列表) {

                方法体;        

        }

public class VarParameter {

    public static void main(String[] args) {
        VarParameterTools test = new VarParameterTools("你好",23);
        System.out.println("test的name:" + test.name);
        System.out.println("test的age:" + test.age);
    }
}

class VarParameterTools{
    int age;
    String name;

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

7.3 注意

        1、构造器的修饰符可以是默认的,也可以是public、protectec、private;

        2、构造器没有返回值;

        3、方法名和类的名字必须一致;

        4、形参列表和成员方法一样的规则;

        5、构造器的调用,由系统完成。 

7.4 构造器使用细节(7点)

        1、一个类可以定义多个不同的构造器,即构造器方法的重载;

        2、构造器名要与类名相同;

        3、构造器没有返回值;

        4、构造器是完成对象的初始化,而不是创建对象;

        5、在创建对象时,系统自动调用该类的构造器(构造方法);

public class VarParameter {

    public static void main(String[] args) {
        VarParameterTools test = new VarParameterTools("你好",23);
        System.out.println("test的name:" + test.name);
        System.out.println("test的age:" + test.age);

        VarParameterTools test2 = new VarParameterTools("你好,这是第二个test");
        System.out.println("test2的name:" + test2.name);
        System.out.println("test2的age:"+test2.age);
    }
}

class VarParameterTools{
    int age;
    String name;

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

    public VarParameterTools(String name){
        this.name = name;
    }
}

         6、如果程序员没有定义构造器,系统会默认自动给类生成一个默认无参构造器(也叫默认构造器);

        7、一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器了,除非自己再定义一次无参构造方法。(可用javap命令来反编译查看验证)

八、this关键字

8.1 问题描述

        当我们需要方法中的形参名与对象的属性名一样时,根据变量作用域的就近原则,会出问题,方法中的代码块只会对局部变量进行操作。

public class VarParameter {

    public static void main(String[] args) {
        VarParameterTools test = new VarParameterTools("你好",23);
        System.out.println("test的name:" + test.name);
        System.out.println("test的age:" + test.age);
    }
}

class VarParameterTools{
    int age;
    String name;

    public VarParameterTools(String name, int age){
        age = age;
        name = name;
    }
}

8.2 this简介

        Java虚拟机会给每个对象分配this,代表当前对象。简单来说,哪个对象调用this,this就代表谁。

(可以这么理解:this是对象的一个隐藏的属性,其指向自己本身。)

public class VarParameter {

    public static void main(String[] args) {
        VarParameterTools test = new VarParameterTools("你好",23);
        System.out.println("test的name:" + test.name);
        System.out.println("test的age:" + test.age);
    }
}

class VarParameterTools{
    int age;
    String name;

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

 8.3 this的使用细节(5点)

        1、this关键字可以访问本类的属性、方法和构造器;

        2、this用于区分当前类的属性和局部变量;

        3、访问成员方法的语法:this.方法名(参数列表);

        4、访问构造器的语法:this(参数列表);

        (注意只能再构造器中使用,即只能在构造器中调用另外一个构造器。)

        (且使用了这种语法,该语句必须放在第一句。)

public class VarParameter {

    public static void main(String[] args) {
        VarParameterTools test = new VarParameterTools();
        System.out.println("test的name:" + test.name);
        System.out.println("test的age:" + test.age);
    }
}

class VarParameterTools{
    int age;
    String name;
    public VarParameterTools(){ //访问构造器语法:this(参数列表),必须是第一条语句
        this("你好,这是测试",24);
    }

    public VarParameterTools(String name, int age){
        System.out.println("调用了VarParameterTools(String name, int age)");
        this.age = age;
        this.name = name;
    }
}

        5、this不能在类定义的外部使用,只能在类定义的方法中使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值