Java面向对象编程(二)方法和封装

二、方法和封装

2.1 构造方法

概念和使用

// 构造方法名与类名完全相同,且没有返回值,连void也不许有
// 可以用权限修饰符(参见单例设计模式,用private修饰构造方法)
class 类名 {
	类名(形参列表) {
		构造方法体;
	}
}

默认构造方法

  • 当一个类中没有定义任何构造方法时,编译器会自动添加一个无参空构造构造方法,叫做默认/缺省构造方法,如:Person(){}
  • 若类中出现了构造方法,则编译器不再提供任何形式的构造方法

作用

  • 使用new关键字创建对象时会自动调用构造方法实现成员变量的初始化
  • 当我们无需自行初始化时,使用编译器提供的默认构造方法即可
/*
	编程实现Person类的定义
*/
public class Person {
	
	String name; // 用于描述姓名的成员变量
	int age;	 // 用于描述年龄的成员变量
	
	// 自定义无参构造方法
	Person(){} 
	
	// 自定义有参构造方法
	Person(String s, int i) {
		//System.out.println("hi! 我是自定义的构造方法");
		name = s;
		age = i;
	}
	
	// 自定义成员方法实现所有特征的打印
	void show() {
		System.out.println("我是" + name + ",今年" + age + "岁了!");
	}
	
	public static void main(String[] args) {
		
		// 1.声明一个Person类型的引用指向Person类型的对象
		Person p1 = new Person("张飞", 30);
		// 打印特征
		p1.show(); // 张飞 30
		
		Person p2 = new Person("关羽", 32);
		p2.show(); // 关羽 32
		
		Person p3 = new Person();
		p3.show(); // 0 null
	}
}

2.2 方法重载(Overload)

  • 若方法名称相同参数列表不同,这样的方法之间构成重载关系
  • 方法重载主要形式体现在:
    • 参数个数不同
    • 参数类型不同
    • 参数顺序不同
    • 与 返回值类型、形参变量名无关,但建议返回值类型最好相同
  • 判断方法能否构成重载的核心:调用方法时能否加以区分
  • 构造方法肯定是重载
/*
    编程实现方法重载主要形式的测试
 */
public class OverloadTest {
	
	// 自定义成员方法
	void show() {
		System.out.println("show()");
	}
	void show(int i) { // ok  体现在方法参数的个数不同
		System.out.println("show(int)");
	}
	void show(int i, double d) { // ok  体现在方法参数的个数不同
		System.out.println("show(int, double)");
	}
	void show(int i, int j) { // ok  体现在方法参数的类型不同
		System.out.println("show(int, int)");
	}
	void show(double d, int i) { // ok  体现在方法参数的顺序不同
		System.out.println("show(double, int)");
	}
	/*
	void show(double a, int b) { // error 与参数变量名无关
		System.out.println("show(double, int)");
	}
	*/
	/*
	int show(double d, int i) { // error, 与返回值类型无关
		System.out.println("show(double, int)");
	}
	*/
	
	public static void main(String[] args) {
		
		// 1.声明OverloadTest类型的引用指向该类型的对象
		OverloadTest ot = new OverloadTest();
		// 2.调用show方法
		ot.show();
		ot.show(66);
		ot.show(66, 3.14);
		ot.show(66, 118);
		ot.show(3.14, 118);
		//ot.show(3.14, 66);
	}
}

2.3 案例:Person类中重载的使用

/*
	编程实现Person类的定义
*/
public class Person {
	
	String name; // 用于描述姓名的成员变量
	int age;	 // 用于描述年龄的成员变量

	// 自定义成员方法实现年龄增长一岁的行为
	void grow() {
		age++;
	}
	
	// 自定义成员方法实现年龄增长参数指定数值的行为
	void grow(int i) {
		age += i;
	}
	
	// 自定义成员方法实现所有特征的打印
	void show() {
		System.out.println("我是" + name + ",今年" + age + "岁了!");
	}
	
	public static void main(String[] args) {
		
		// 实现方法重载的调用和测试
		p1.grow();
		p1.show();
		p1.grow(2);
		p1.show();
	}
}

2.4 方法重载的实际意义

  • 方法重载的实际意义在于调用者只需要记住一个方法名就可以调用各种不同的版本,来实现不同的功能
  • 如:java.io.PrintStream类中的println方法(其实底层有很多不同的版本)

2.5 this关键字

基本概念

  • 若在构造方法中出现了this关键字,则代表当前正在构造的对象
  • 若在成员方法中出现了this关键字,则代表当前正在调用的对象
  • this关键字本质上就是当前类类型的引用变量(存放对象的内存地址)

工作原理

  • 在构造方法中和成员方法中访问成员变量时,编译器会自动加上 this. 的前缀,而 this. 相当于“我的”,当不同的对象调用同一个方法时,由于调用方法的对象不同,导致this关键字不同,从而this. 方式访问的结果也就不同。

使用方式

方式一(重中之重)
  • 当局部变量名和成员变量名相同时,在方法体内会优先使用局部变量(就近原则),若希望使用成员变量,则需要在成员变量的前面加上 this. 的前缀,明确要求该变量是成员变量
public class Person {

	String name;
	int age;
	// 就近原则(懒人原则)
	// 如果不加 this. ,则以下的两个name都是形参变量,加上this.,则该变量是引用对象的成员变量
	Person(String name. int age) {
		this.name = name;
		this.age = age;
	}
	main方法 ... 
}
方式二(重点)
  • this关键字除了可以通过 this. 的方式调用成员变量和成员方法外,还可以作为方法的返回值
Person GetPerson() {
	// 返回当前调用对象本身
	return this;
	// 等价于以下代码
	// Person tp = new Person();
	// return tp;
}
方式三(了解)
  • 在构造方法的第一行可以使用 this() ; 的方式来调用本类中的其它构造方法
  • 即:可以通过 this(); 来实现构造方法对另一个构造方法的调用,调用有参构造函数时,要加上实参
  • 注:如果两个构造方法相互调用就会报错(错误:递归构造器调用)

2.6 引用变量的注意事项

  • 引用类型变量用于存放对象的地址,可以给引用类型赋值为null,表示不指向任何对象
  • 但是,当某个引用类型变量为null时,对象无法被访问(因为它没有指向任何对象)。此时,如果通过访问成员变量或者调用方法,会报错(NullPointerExcepion)
  • (编译不报错,执行报错)

至此,出现了三种异常
(1) ArithmeticException
(2) ArrayIndexOutOfBoundsException
(3) NullPointerException

2.7 阶乘的计算方式

方式一(递推)

/*
	编程实现累乘的计算并打印
*/
public class Factorial {

	// 自定义成员方法实现参数n的阶乘计算并返回
	int show(int n) {
		int num = 1;
		for(int i=1; i<=n; i++) {
			num += 1;
		}
		return num;
	}
	public static void main(String[] args) {
		
		// 1. 声明Factorial类型的引用指向该类型的对象
		Factorial factorial = new Factorial();
		// 2.调用方法进行计算并打印
		factorial.show(10);
	}
}

方式二(递归)

/*
	编程实现累乘的计算并打印
*/
public class Factorial {

	// 自定义成员方法实现参数n的阶乘计算并返回
	int show(int n) {
		// 当n的数值为1时,则阶乘的结果为1
		
		/*if(1==n) {
			return 1;
		}*/
		if(1==n) return 1; // 上述if结果的简略写法
		// 否则阶乘的结果是 n * (n-1)!
		return n*show(n-1);
	}
	public static void main(String[] args) {
		
		// 1. 声明Factorial类型的引用指向该类型的对象
		Factorial factorial = new Factorial();
		// 2.调用方法进行计算并打印
		factorial.show(10);
	}
}

2.8 递归的原理

  • 递归本质:在方法体内部 直接或间接调用当前方法自身的形式
    在这里插入图片描述

2.9 递归的注意事项

  • (1) 使用递归必须有递归的规律以及退出条件
  • (2) 使用递归必须使得问题简单化,而不是复杂化
  • (3) 若递归影响到程序的执行性能,则使用递推公式取代之

2.10 费氏数列的实现

  • 斐波那契数列
    • 1, 1, 2, 3, 5, 8, 13, 21, …

递归

  • 用递归计算费氏数列时,执行效率很低
// 自定义成员方法实现费氏数列中第n项数值的计算并返回,n由参数确定
int show(int n) {
	if(1==n || 2==n) return 1;
	return show(n-1) + show(n-2)
}

递推

/*
	编程实现斐波那契数列的递推式
*/
public class Fibonacci {
	
	// 自定义成员方法实现费氏数列第n项数值的计算并返回,n由参数指定
	int show(int n) {
		int a = 1;
		int b = 1;
		int c = a + b;
		
		if(1==n || 2==n) {
			System.out.println(1);
			return 1;
		}
			
		for(int i=3; i<n; i++) {
			a = b;
			b = c;
			c = a + b;
		}
		return c;
		
	}
	
	public static void main(String[] args) {
		
		// 1.声明Fibonacci类型的引用指向该类型的对象
		Fibonacci f = new Fibonacci();
		// 2.调用方法并打印结果
		int res = f.show(20);
		System.out.println("计算的结果是:" + res);
		
	}
}

2.11 代码的拆分实现

  • 代码拆分(功能类 + 测试类),以便于功能的复用
  • 这是编程规范
  • 以上面的代码为例
/*
	编程实现斐波那契数列的计算
	封装类/功能类
*/
public class Fibonacci {
	
	// 自定义成员方法实现费氏数列第n项数值的计算并返回,n由参数指定
	int show(int n) {
		int a = 1;
		int b = 1;
		int c = a + b;
		
		if(1==n || 2==n) {
			System.out.println(1);
			return 1;
		}
			
		for(int i=3; i<n; i++) {
			a = b;
			b = c;
			c = a + b;
		}
		return c;
	}
}
/*
	编程实现对费氏数列的测试 
	测试类
*/
public class FibonacciTest {
	public static void main(String[] args) {
	
		// 1.声明Fibonacci类型的引用指向该类型的对象
		Fibonacci f = new Fibonacci();
		// 2.调用方法并打印结果
		int res = f.show(20);
		System.out.println("计算的结果是:" + res);
	}
}

2.12 封装(重中之重)

概念

  • 通常情况下可以在测试类给成员变量赋值一些合法但不合理的数值,无论是编译阶段还是运行阶段都不会报错或给出提示,与现实生活不符合
  • 为了避免上述错误的发生,就需要对成员变量进行密封包装,来隐藏成员变量的细节以及保证成员变量数值的合理性,该机制叫做封装。

封装的实现流程

  • (1) 私有化成员变量,使用 private关键字 修饰,表示私有的,该成员变量只能在 当前类 的内部使用
  • (2) 提供公有的 getset 方法,并在方法体中进行合理值的判断,使用 public关键字 修饰,表示该方法可以在任意位置使用
    • 什么修饰符都没有叫做默认的访问权限,级别介于private和public之间(一般加上public,好过什么都没有)
  • (3) 在构造方法中调用 set 方法进行合理值的判断
/*
	编程实现Student类的封装 封装类
*/
public class Student {
	
	// 1.私有化成员变量,使用private关键字修饰
	// private关键字修饰表示私有的含义,也就是该成员变量只能在当前类的内部使用
	private int id;		// 用于描述学号的成员变量
	private String name;// 用于描述姓名的成员变量
	
	// 3.在公有的构造方法中调用set方法
	public Student() {}
	public Student(int id, String name) {
		setId(id);
		setName(name);
	}
	
	// 2.提供公有的get和set方法,并在方法体中进行合理的判断
	// 使用public关键字修饰表示私有的含义,也就是该方法可以任意位置使用
	public int getId() {
		return id;
	}
	public void setId(int id) {
		if(id > 0) {
			this.id = id;
		} else {
			System.out.println("学号不合理!");
		}
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	// 自定义成员方法实现特征的打印
	// 什么修饰符都没有叫做默认的访问权限,级别介于private和public之间
	public void show() {
		//System.out.println("我是" + Name + ", 我的学号是" + id);
		System.out.println("我是" + getName() + ", 我的学号是" + getId()); // 利于代码维护
	}
	
}
/*
	编程实现Student类的测试
*/
public class StudentTest {
	
	public static void main(String[] args) {
		
		// 1.声明Student类型的引用指向Student类型的对象
		Student s1 = new Student();
		// 2.对成员变量进行赋值并打印
		s1.setId(-1000);
		s1.setName("张飞");
		s1.show();
		
		System.out.println("-----------------------------");
		// 3.使用有参方式构造对象并打印特征
		Student s2 = new Student(-1001, "关羽");
		s2.show();
	}
}

2.13 案例:学生信息的录入

  • 提示用户输入班级的学生人数以及每个学生的信息,学生的信息有:学号、姓名,最后分别打印出来
  • Student类的封装 前面已经写好了
  • 提示:Student[ ] arr = new Students[num];
/*
	编程实现学生信息的录入和打印
*/

import java.util.Scanner;

public class StudentTest2 {
	
	public static void main(String[] args) {
			
		// 1.提示用户输入学生的人数并使用变量记录
		System.out.println("请输入学生的人数:");
		Scanner sc = new Scanner(System.in);
		int num = sc.nextInt();
		
		// 2.根据学生人数声明对应的一维数组
		// int[] arr = new int[3]; - 表明声明一个长度为3类型为int的一维数组
		// 数组中的每个元素都是int类型,也就是说数组中的每一个元素可以看作是一个
		// int类型的变量,使用整数数据进行初始化 arr[0] = 10;
		// 下面的代码是声明一个长度为num元素类型为Student类型的一维数组
		// 数组中的每一个元素都是Student类型,即数组中每个元素都可看作是Student类型的变量
		// 初始化:arr[0] = new Student();
		Student[] arr = new Student[num];
		
		// 3.提示用户输入每个学生的信息(学号 姓名)并记录到一维数组中
		for(int i=0; i<num; i++) {
			System.out.println(arr[i]); // null
			System.out.println("请输入第" + (i+1) + "个学生的信息(学号 姓名)");
			arr[i] = new Student(sc.nextInt(), sc.next()); 
		}
		// 4.打印所有学生信息
		for(int i=0; i<arr.length; i++) {
			// 方法一:
			//arr[i].show();
 			// 方法二:
 			System.out.println("学号:" + arr[i].getId() + " " + "姓名" + arr[i].getName());
	    }
	}
}

2.14 JavaBean的概念(了解)

  • JavaBean是一种Java语言写成的可重用组件,其他Java类可以通过反射机制发现和操作这些JavaBean的属性
  • JavaBean本质上就是符合以下标准的Java类(封装类):
    • 类是公共的(public)
    • 有一个无参的公共的构造器( 如:public Student() {} )
    • 有属性,且有对应的 get、set 方法

总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值