Java基础(6)-类和对象、递归

类和对象

类就是数据类型,比如Cat;对象就是一个具体的实例。

class Cat{
    String name;
    String color;
    int age;
}
Cat cat = new Cat();
//Cat cat;  开辟空间Cat【空】
//cat = new Cat();   空间分配,把地址放进去Cat【地址】  先声明在创建
//cat 是对象名(对象引用);
//new Cat() 创建的对象空间(数据)才是真正的对象
cat.name = "小白";
cat.color = "白色";
cat.age = 12;
对象在内存中的存在形式

对象在内存中的存在形式

属性/成员变量
  • 从概念或叫法上看,成员变量 = 属性 = field(字段),(即成员变量是用来表示属性的属性的)
  • 属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象,数组)。

细节

  1. 属性的定义语法和变量一样,示例 访问修饰符(控制属性的访问范围public 默认 protected private) 属性类型 属性名;
  2. 属性的定义类型可以为任意类型,包含基本类型或引用类型;
  3. 属性如果不赋值,有默认值,规则和数组一致。
类和对象的内存分配机制

在这里插入图片描述

Java内存结构分析

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

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

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

Java创建对象的流程

1、先加载Person类信息(属性和方法信息,只会加载一次)

2、在堆中分配空间,进行默认初始化(看规则)

3、把地址赋给 p , p 就指向对象

4、进行指定初始化,比如p.name = “jack” p.age = 10

Person b = new Person();
b = null;
System.out.println(b.age);
//最后一句会抛出异常,它不指向任何空间!!!!!!!!!!!!!

成员方法

class Person{
    String name;
    int age;
    //公开 没有返回值 方法名 形参列表
    //方法不调用是没有用的,不会输出
    public void speak(){
        System.out.println("我是一个好人");
    }
    public void cal01(){
        int sum = 0;
        for(int i = 1;i <= 1000;i++){
            sum += i;
        }
        System.out.println("sum=" + sum);
    }
    public void cal02(int n){
        int sum = 0;
        for(int i = 1;i <= n;i++){
            sum += i;
        }
        System.out.println("sum(n)=" + sum);
    }
    //int 表示该方法返回一个int值
    public int getSum(int num1,int num2){
        int res = num1 + num2;
        return res;
    }
}
Person p1 = new Person();
p1.speak();//调用方法
p1.cal01();
p1.cal02(10);
int returnRes = p2.getSum(10,20);//返回的值赋给returnRes
System.out.println("returnRes=" + returnRes);
方法调用机制原理

在这里插入图片描述

成员方法细节
  • 访问修饰符,作用是控制方法的使用范围。如果不写则默认访问(public private 默认 protected)
  • 一个方法最多有一个返回值,如果返回多个结果,则返回数组
  • 返回类型可以是任意类型,可以是基本类型也可以是引用类型(数组,对象)
  • 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为return 值,而且要求返回值类型和return的值类型一致或兼容
  • 如果方法是void,则方法体中可以没有return语句,或者只写return;
  • 方法名遵守驼峰命名法
形参列表细节
  • 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开
  • 参数类型可以为任意类型,基本数据类型和引用数据类型
  • 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数
  • 方法定义时的参数称为形式参数,简称形参;方法调用时的参数称为实际参数,简称实参;实参和形参的类型要一致兼容,顺序和个数相同。
方法体细节
  • 里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!即方法不能嵌套定义。
方法调用细节
  • 同一个类中的方法调用,直接调用即可(不需要创建对象)
  • 跨类中的方法A类调用B类方法:需要通过对象名调用
  • 特别说明一下:跨类的方法调用和方法的访问修饰符相关

成员方法传参机制

int a = 10;
int b = 20;
AA obj = new AA();
obj.swap(a,b);// 10 20
System.out.println("外" + a + " " + b);
class AA{
    public void swap(int a,int b){
        System.out.println("内1" + a + " " + b);// 10 20
        int tmp = a;
        a = b;
        b = tmp;
        System.out.println("内2" + a + " " + b);// 20 10
    }
}

在这里插入图片描述

主方法中的a和b与swap方法中的a和b空间是独立的,互不影响。

B b = new B();
int[] arr = {1,2,3};
b.test100(b);
for(int i = 0;i < arr.length;i++){
    System.out.print(arr[i] + "\t");
}
System.out.println();//  200 2 3
Person p = new Person();
p.age = 10;
p.name = "jack";
b.test200(p);
System.out.print(p.age);//会输出10,因为test200方法中的p指向空,并不代表主方法中的p指向空
                       //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
class B{
    public void test100(int[] arr){
        arr[0] = 200;
        for(int i = 0;i < arr.length;i++){
            System.out.print(arr[i] + "\t");
        }
        System.out.println();//  200 2 3
    }
    public void test200(Person p){
        p = null;
    }
}


class Person{
    String name;
    int age;
}

在这里插入图片描述

引用数据类型(数组和对象)在方法中改变后会真实改变的。

可以通过对象比较来看两个对象是不是一个对象

递归

递归就是方法自己调用自己,每次调用时传入不同的变量。

public int test1(int n){
	 	if(n == 0 || n == 1){
	 		return 1;
	 	}else{
	 		return n * test1(n-1);
	 	}
	 }MyTools tool = new MyTools();
tool.test(4);
//输出2
class MyTools{
	 public void test(int n){
	 	if(n > 2){
	 		test(n - 1);
	 	}else{
	 	System.out.println("n = " + n);
	 }
}

    
MyTools tool = new MyTools();
tool.test(4);
//输出2,3,4
class MyTools{
	 public void test(int n){
	 	if(n > 2){
	 		test(n - 1);
	 	}
	 	System.out.println("n = " + n);
}
   
    
//阶乘
public int test1(int n){
	 	if(n == 0 || n == 1){
	 		return 1;
	 	}else{
	 		return n * test1(n-1);
	 	}
	 }
遵守的规则
  • 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
  • 方法的局部变量是独立的,不会相互影响,比如n变量
  • 如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据
  • 递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError
  • 当一个方法执行完毕,或者遇到return就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。
猴子吃桃
//猴子每天吃当天桃子的一半加一,第十天剩一个桃子,求最初桃子多少个
public int monkey(int n){
	 	if(n == 10){
	 		return 1;
	 	}else if(n >= 1 && n <= 9){
	 		return (monkey(n + 1) + 1) * 2;
	 	}else{
	 		return -1;
	 	}
	 }
老鼠出迷宫
public boolean findway(int[][] map,int i,int j){
	 	if(map[6][5] != 2){
	 		if(map[i + 1][j] == 0){
	 			map[i + 1][j] = 2;
	 			return findway(map,i + 1,j);
	 		}else if(map[i][j + 1] == 0){
	 			map[i][j + 1] = 2;
	 			return findway(map,i,j + 1);
	 		}else if(map[i - 1][j] == 0){
	 			map[i - 1][j] = 2;
	 			return findway(map,i - 1,j);
	 		}else if(map[i][j - 1] == 0){
	 			map[i][j - 1] = 2;
	 			return findway(map,i,j - 1);
	 		}else{
	 			map[i][j] = 3;
	 			return false;
	 		}
	 	}else{
		 	for(int m = 0;m < 8;m++){
				for(int n = 0;n < 7;n++){
					System.out.print(map[m][n] + " ");
			}
				System.out.println();
			}
	 		return true;
	 	}
	 }




老师:可以回溯,因为在走下一步之前,会检验走下一步是否能到达终点,如果不能,就不走,这样就会防止走向死胡同。
public boolean findway(int[][] map,int i,int j){
    if(map[6][5] == 2){
        return true;
    }else{
        if(map[i][j] == 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;
        }
    }
}
//求解最短路径:
//(1)穷举(2)根据图的知识
汉诺塔和八皇后
public void hanoiTower(int num,char a,char b,char c){
	    	if(num == 1){
	    		System.out.println("从" + a + "柱将砖头移到" + c + "柱");
	    	}else if(num > 1){
	    		hanoiTower(num - 1, a, c, b);
	    		hanoiTower(1, a, b, c);
	    		hanoiTower(num - 1, b, a, c);
	    	}else{
	    		System.out.println("输入错误");
	    	}
	    }

重载

java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致。

细节
  1. 方法名必须相同;
  2. 参数列表必须不同(参数类型或个数或顺序,至少有一样不同,参数名无要求)
  3. 返回类型:无要求
System.out.println(tool.max(91,5.666,88));//优先执行第二个方法,因为不需要发送转化
class Tools{
    public double max(double i,double j,double q){
		double temp = max(i,j);
		return temp > q?temp:q;
	}
    public double max(double i,double j,int q){
		double temp = max(i,j);
		return temp > q?temp:q;
	}
}

可变参数

java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。

基本语法:访问修饰符 返回类型 方法名(数据类型···形参名){ }

m.sum(1,5,100);
class M{
    public int sum(int... nums){
        //接收的int型是0到多,使用可变参数时,可以当作数组使用。
        int res = 0;
        for(int i = 0;i < nums.length;i++){
            res += nums[i];
        }
        return res;
    }
}
细节
  1. 可变参数的实参可以是0个或者任意多个;
  2. 可变参数的实参可以为数组;
  3. 可变参数的本质就是数组;
  4. 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后;
  5. 一个形参列表中只能出现一个可变参数。

作用域

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

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

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

  • Java中作用域的分类:全局变量,也就是属性,作用域为整个类体,属性在定义时,可以直接赋值;

    局部变量,也就是除属性之外的其他变量,作用域为定义它的代码块中;

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

规则
  • 属性和局部变量可以重名,访问的时候遵守就近原则;

  • 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名;

  • 属性生命周期长,伴随对象的创建而创建,销毁而销毁,局部变量生命周期短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁,即在一次方法调用过程中。

  • 作用域范围不同,全局变量/属性可以被本类使用,或其他类使用(通过对象调用----新建或对象作为参数),局部变量只能在本类中对应的方法中使用;

  • 修饰符不同,全局变量/属性可以加修饰符,局部变量不可以加修饰符。

构造器

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

​ 方法体;

}

注意
  • 构造器的修饰符可以默认,也可以是public protected private

  • 构造器没有返回值;

  • 方法名 和类名字必须一样

  • 参数列表 和 成员方法一样的规则;

  • 构造器的调用系统完成

构造方法又叫构造器,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化,它有几个特点:(1)方法名和类名相同;(2)没有返回值;(3)在创建对象时,系统会自动的调用该类的构造器完成对对象的初始化。

class Person{
	String name;
	int age;
	public Person(String pname,int page){
		System.out.println("构造器被调用。");
		name = pname;
		age = page;
	}
}
细节
  • 一个类可以定义多个不同的构造器,即构造器重载,比如定义一个只指定人名不指定年龄的构造器;
  • 构造器名和类名要相同;
  • 构造器没有返回值;
  • 构造器是完成对象的初始化,并不是创建对象;
  • 在创建对象时,系统自动的调用该类的构造方法。
  • 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如Person(){},使用javap指令反编译看看;
  • 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下,即Person(){}
Dog dog = new Dog();
class Dog{
	public Dog(String dname){
		//...
	}
}
//这里会报错!!!!!!因为默认构造器已经被覆盖了,不能使用了,除非显式再定义一下,如下:
Dog dog = new Dog();
class Dog{
	public Dog(String dname){
		//...
	}
    Dog(){
    }
}

对象创建的流程分析

在这里插入图片描述

  • 加载Person类信息(Person.class),只会加载一次
  • 在堆中分配空间(地址)
  • 完成对象初始化【3.1默认初始化age = 0,name = null ;3.2显式初始化age = 90,name = null;3.3构造器初始化age = 20,name = ”小倩“】
  • 在对象在堆中的地址返回给p,p是对象名,也可以理解为对象的引用

this

java虚拟机会给每个对象分配this,代表当前对象。

哪个对象调用,this就代表哪个对象。

在这里插入图片描述

class Dog{
    public String name;
    public int age;
    public Dog(String name,int age){
        this.name = name;
        this.age = age;
    }
    public void info(){
        System.out.println(this.name + "\t" + this.age + "\t" + "当前对象的hashCode是:" + this.hashCode());
    }
}
注意事项和使用细节
  • this关键字可以用来访问本类的属性、方法、构造器;
  • this用于区分当前类的属性和局部变量;
  • 访问成员方法的语法:this.方法名(参数列表)
  • 访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器,必须放在第一条);
  • this不能在类定义的外部使用,只能在类定义的方法中使用。
class T{
    String name = "jack";
    int num = 10;
    //访问构造器语法:this(参数列表);必须放置第一条语句
    public T(){
        this("jack",100);//第一条!!!!!!!!!!!!!!!!!!
        System.out.println("T() 构造器");
        //这里访问T(String name,int age)
    }
    public T(String name,int age){
        System.out.println("T(String name,int age) 构造器");
    }
    
    
    public void f3(){
        String name = "Smith";
        System.out.println("name = " + name + " num = " + num);//就近原则访问
        System.out.println("name = " + this.name + " num = " + this.num);//就是指属性
    }
}

判断两个对象是否相同:

class person{
	String name;
	int age;

	public person(String pname,int page){
		System.out.println("构造器被调用。");
		name = pname;
		age = page;
	}

	public boolean compareTo(person p){
		//if(this.name.equals(p.name) && this.age == p.age){
			//return true;
		//}else{
			//return false;
		//}
        return this.name.equals(p.name) && this.age == p.age;
	}
}

作业

class A01{
    //找出一个数组里的最大值
	//可以考虑到代码的健壮性,即先判断输入的数组是不是空数组{},还有该数组是不是就是null
	public Double max(double[] array){
		if(array != null && array.length > 0){
			double m = array[0];
			for(int i = 1;i < array.length;i++){
				if(array[i] > m){
					m = array[i];
				}
			}
			return m;
		}else{
			return null;
		}
	}
}
public class Test{
    int count = 9;
    public void count1(){
        count = 10;//这个count就是指属性,将属性改成10
        System.out.println("count1 = " + count);
    }
    public void count2(){
        System.out.println("count1 = " + count++);
    }
    
    public static void main(String args[]){
        //1.new Test()是匿名对象,在堆里,只能用一次,使用后就不能再使用;
        //2.new Test().count1()指创建好匿名对象后,就调用count1()
        new Test().count1();//输出count1 = 10
        Test t1 = new Test();
        t1.count2();//输出count1 = 9,先输出再自加,变成10
        t1.count2();//输出count1 = 10,先输出再自加,变成11
    }
}
class Demo{
    int i = 100;
    public void m(){
        int j = i++;
        System.out.println("i=" + i);
        System.out.println("j=" + j);
    }
}
class Test{
    public static void main(String args[]){
        Demo d1 = new Demo();
        Demo d2 = d1;
        d2.m();//输出i = 101   j = 100
        System.out.println(d1.i);//101
        System.out.println(d2.i);//101
    }
}
//构造器的复用
class Employee{
	String name;
	char gender;
	int age;
	String position;
	double salary;

	public Employee(String name,char gender,int age,String position,double salary){
		this(name,gender,age);//this只能放在构造器中的第一行
		//this(position,salary);第二行,错误!!!!!!!!!!!!!!!!
		this.position = position;
		this.salary = salary;
		// this.name = name;
		// this.gender = gender;
		// this.age = age;
		// this.position = position;
		// this.salary = salary;
	}

	public Employee(String name,char gender,int age){
		this.name = name;
		this.gender = gender;
		this.age = age;
	}

	public Employee(String position,double salary){
		this.position = position;
		this.salary = salary;
	}
}
class Circle{
	double radius;

    public Circle(){//无参构造器,如果使用的话要重新声明
        
    }
    
	public Circle(double radius){
		this.radius = radius;
	}

	public double findArea(){
		return Math.PI * this.radius * this.radius;
	}

	public void changeR(double r){//可以修改半径的方法
		this.radius = r;
	}
}

class PassObject{
	public void printAreas(Circle c,int times){
		for(int i = 1;i <= times;i++){
			System.out.println("半径 = " + (double)i);
			c.changeR(i);
			System.out.println("对应的面积 = " + c.findArea());
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值