9 面对对象-上篇

1. 类与对象

1.1 看一个问题

张老太养了两只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年10岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示 张老太没有这只猫猫。
如果没有面对对象.我要要用计算机语言来描述这个猫我们会这样子思考:
1)单独的定义变量解决
2)使用数组解决
代码实现:

/*
		张老太养了两只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年10岁,花色。
		请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。
		如果用户输入的小猫名错误,则显示 张老太没有这只猫猫。

		*/

		//1. 使用变量
		String cat1Name = "小白";
		int cat1Age = 3;
		String cat1Color = "白色";
		
		String cat2Name = "小花";
		int cat2Age = 10;
		String cat2Color = "花色";

		//分析方案的问题

		//1. 因为一只猫有不同的属性(名字,年龄,颜色), 每种属性的数据类型可能不一样
		//   上面的方法,把一只猫使用三个不相干的变量来存储,因此不利于数据的管理和维护

		//2. 如果将来的猫的属性,有变化,我们的代码的维护很差

		//3. 如果我们希望猫有一定的行为(方法), 没有管理



		//2. 考虑数组

		String[] names = {"小白", "小花"};
		int[] ages = {3, 10};
		String[] colors = {"白色", "花色"};

//分析上面的问题
//1. 从形式上看,我们使用数组管理的猫的属性,但是因为数组不能混用,因此数据仍然是独立.
//2. 如果我们希望猫有一定的行为(方法), 没有管理
//这时,java的设计者,给出一种新的数据类型,-》 类(class) [可以将不同的数据类型,统一的管理]

我们用面对对象的思想,我们会这样子考虑:
1). 把所有猫共有是特殊统一描述出来,比如名字,性别…
2). 把猫的行为也描述出来,比如猫会叫…
3).把上面的两者整合到一起,组成一个猫的模板,较猫类.
代码:

/定义一个Cat类
//看出一个Cat类,包含了你需要的数据(属性)
class Cat {
	//属性
	public String name;
	public short age;
	public String color;

	//?方法
}


//使用Cat 来解决

//使用面向对象的方法来解决

		//(1) 创建Cat类的对象
		System.out.println("========面向对象编程解决问题========");
		Cat cat1 = new Cat();   // int num1 = 10;
		cat1.name = "小白";
		cat1.age = 3;
		cat1.color = "白色";

		Cat cat2 = new Cat();   // int num2 = 10;
		cat2.name = "小花";
		cat2.age = 10;
		cat2.color = "花色";

		System.out.println("第一只猫的信息: name = "+cat1.name +
			" age = "+cat1.age+" color = " + cat1.color);

		System.out.println("第二只猫的信息: name = "+cat2.name +
			" age = "+cat2.age+" color = " + cat2.color);

这样子我们就能用猫类来统一规划猫这个类别.而上面的cat1和cat2,就是这个猫类的实例对象.

1.2 面对对象的概念

对象的含义是指具体的某一个事物,即在现实生活中能够看得见摸得着的事物。在面向对象程序设计中,对象所指的是计算机系统中的某一个成分。在面向对象程序设计中,对象包含两个含义,其中一个是数据,另外一个是动作。对象则是数据和动作的结合体。对象不仅能够进行操作,同时还能够及时记录下操作结果

1.3 类与对象的关系

1). 类是抽象的,概念的,代表一类事物,比如人类,猫类…
2). 对象是具体的,实际的,代表一个具体事物,比如,小明(这是人,具体指某个人)
3). 类是对象的模板,对象是类的一个实体,对应一个实例.

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

我们看下面一段代码:

Cat cat=new Cat();
cat.name=“小白”;
cat.age=12;
cat.color=“red”;

这个cat在内存存在的形式如图:
在这里插入图片描述

1.5 类的定义

一个类的定义如下:

package 包名;
访问修饰符 class   类名 extends 父类 implements 接口名 {
  	成员变量;
  	构造方法;
  	成员方法;
}

上面类的各个部分我们会在后面会一一介绍.

2. 属性/成员变量

2.1 基本介绍

从概念或叫法上看: 成员变量 = 属性 = field (字段)(即 成员变量是用来表示属性的.下面我们都会用属性,你们知道这几个词都是一回事就行了.

那怎么定义属性呢看下面:

class cat{
	//属性定义
	String name;//这是猫类的属性,代表名字
	int age;//这是猫类的属性,代表年龄
	String color;//这是猫类的属性,代表颜色
}

属性是类的一个组成部分,一般是基本数据类型,也可是引用类型。比如上面定义猫类 的 int age 就是属性.

2.2 属性定义注意事项和细节说明

1)属性的定义语法同变量,示例:属性类型 属性名 [=值]

class 类名 {
访问修饰符 数据类型 变量名;
}

说明
①类名:是程序员指定的,一般使用大驼峰法. 比 CatLover
②访问修饰符: 在面向对象中,访问修饰符的作用是控制 成员变量的访问范围,一共有四种,public > protected > 默认 > private
③数据类型, 只要是java 中的数据类型都可以使用,
④变量名, 变量名的命名方式就是小驼峰
2)属性的定义类型可以为任意类型,包含基本类型或引用类型
3) 属性如果不赋值,有默认值,规则和数组一致.规则如下:

int , short , long, byte ==> 默认值为 0
float, double ==>默认值为 0.0
boolean => 默认值为 false
char => 默认值 为'\u0000' 空字符.
String ==> 默认值 null
自定类的对象比如Car ==>  默认值 null
  1. 不同对象的属性是独立,互不影响,一个对象对属性的更改,不影响另外一个。
    比如:
Cat cat1 = new Cat();
Cat cat2 = new Cat();;
//上面创建了两个cat,cat1和cat2,但这两个cat是相互独立的,在内存中它们存储在不同空间.

2.3 如何创建对象

先声明再创建
①对象声明: 类名 对象名;
②对象创建: 对象名=new 类名();

Cat cat ;
cat = new Cat();

或者可以一步到位:

Cat cat = new Cat();

2.4 如何访问属性

基本语法
对象名.属性名;

2.5 类和对象的内存分配机制

在说这个之前我们先了解一下java 内存结构的简单分析

2.5.1Java内存的结构分析

1)栈:基本类型变量(局部变量)、引用名称
2)堆:对象
3)方法区:类信息

2.5.2 分析

我们来看一段代码:

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

		Person p1 = new Person();
		p1.name = "tom";
		p1.age = 10;
		Person p2 = p1; //?

		Car car = new Car();
		car.name = "宝马500";
		car.price = 77777;

		p1.car = car;


		System.out.println("p2.age=" + p2.age); // 10
		System.out.println("p1.age=" + p1.age); // 10

		//p1.car.name = "奔驰1000";
		//p2.car.name = "奔驰2000";
		car.name = "奔驰3000"; 
		System.out.println("1人的车信息为=" + p1.car.name); // "奔驰3000"
		System.out.println("2人的车信息为=" + p2.car.name); // "奔驰3000"
	}
}

class Person {
	public String name;
	public int age;
	public Car car; //引用类型 null
}

class Car {
	public String name;
	public int price; 
}

对上面代码的内存分析图:
在这里插入图片描述

2.6 java创建对象的流程分析

看下面的代码:

Person p1 = new Person();
p1.name = "xx"; p1.age = 10;

流程分析:

  1. 先加载Person类结构的信息(属性信息,方法信息)
  2. 在堆中分配内存空间
  3. 进行初始化操作[给默认值]
    4)进行默认值得修改
    5)将new得到堆中的地址赋给p1变量

3 成员方法/成员函数

在某些情况下,我们要需要定义成员方法。比如人类:除了有一些属性外( 年龄,姓名…),我们人类还有一些行为比如:可以说话、跑步…,通过学习,我们人类还可以做算术题。这时就要用成员方法才能完成。现在要求对Person类完善。

3.1 成员方法快速入门

1)添加speak 成员方法,输出 我是一个好人
2)添加jisuan 成员方法,可以计算从 1+…+1000的结果
3)改jisuan 成员方法,该方法可以接收一个数n,计算从 1+…+n 的结果
4)添加getSum成员方法,可以计算两个数的和

代码如下:

public class MethodDemo{  //成员方法的案例, 方法 == 成员方法 
	
	public static void main(String [] args) {
			
			Person p = new Person();

			//调用成员方法的基本语法 对象名.方法名()
			p.speak(); // 我是一个好人
			p.jisuan(); // 输出结果

			//100: 就是实际参数,简称 实参.
			p.jisuan2(100); // 55
			//如果有多个参数,使用 , 间隔
			p.getSum(1, 5);
	}
}

//除了有一些属性外( 年龄,姓名..),
class Person {
	public String name;
	public short age;
	//添加speak 成员方法,输出 我是一个好人

	//说明
	//1. public 是访问控制修饰符,用于控制成员方法的访问范围
	//2. void : 表示该成员方法没有返回值.
	//3. speak(): speak 表示成员方法的名字 (): 该方法不接受任何的参数.
	//4. {} 表示方法体,该方法完成的任务,是放在{} 内.
	public void speak()  {
		System.out.println("我是一个好人");
	}

	//添加jisuan 成员方法,可以计算从 1+..+1000的结果
	//1. 一个类中,可以有很多的成员方法
	//2. 方法体可以有任意的控制结构,比如分支,循环等等
	public void jisuan() {
		int res = 0;
		for (int i = 0; i <= 1000  ; i++ )	{
			res += i;
		}
		System.out.println("计算的结果是=" + res);
	}

	//改jisuan 成员方法,该方法可以接收一个数n,计算从 1+..+n 的结果

	//说明
	//1. public void jisuan2(int n) 的 (int n)  表示方法的形参列表
	//2. 如果有多个形参则,使用,隔开
	//3. 形参的格式  (形参类型 形参名, 形参类型 形参名...)
	//4. 形参的全名是 形式参数

	public void jisuan2(int n) {
		int res = 0;
		for (int i = 0; i <= n  ; i++ )	{
			res += i;
		}
		System.out.println("计算的结果是=" + res);
	}

	public void getSum(int n1, int n2) {
		int res = n1 + n2;
		System.out.println("计算的结果是=" + res);
	}
}

3.2 方法的调用机制原理

在这里插入图片描述

3.3 成员方法的好处

1)提高代码的复用性
2)可以将实现的细节封装起来,然后供其他用户来调用即可。

3.4 成员方法的定义

成员方法也叫成员函数,这里希望大家不要被这两个名词搞晕了
在这里插入图片描述

3.5 注意事项和使用细节

修饰符
可选,如果不写就是默认的,【public ,protected,默认,private】=》具体放在继承讲
返回类型

  1. 一个方法至多有一个返回值 【如果要返回多个值,怎么办,用数组 】
  2. 返回类型可以为任意类型,包含基本类型或引用类型
  3. 如果方法有返回值,则方法体中最后的执行语句必须为return语句,而且要求返回类型必须和return的值类型一致或兼容.
  4. 如果方法没有返回值,则方法体中可以没有return语句,返回类型要求写void
    方法名
  5. 遵循小驼峰命名法,最好见名知义,表达出该功能的意思即可
    参数列表
  6. 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开(类型 名,类型 名…) 【getSum】
  7. 参数类型可以为任意类型,包含基本类型或引用类型 【findNumInArray(int findNum , int[] arr)】
  8. 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型 的参数!【test100】
    4)方法定义时的参数称为形式参数,简称形参;方法调用时的参数称为实际参数,简称实参,实参和形参的类型、个数、顺序必须一致!
    方法体
    里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!即:方法不能嵌套定义。
    成员方法的调用说明
  9. 同一个类中的方法调用:直接调用即可。比如 print(参数);
  10. 跨类中的方法调用:需要通过对象名调用。比如 对象名.方法名(参数);

3.6 成员方法传参机制

方法的传参机制对我们今后的编程非常重要

3.6.1基本数据类型的传参机制

看一个案例
编写一个方法,交换两个整数的值。
结论及示意图
传递的是值(元素内容),形参的任何改变不影响实参!

public static void main(String[] args) {	
		
		int num1 = 3;
		int num2 = 4;
		test10(num1,num2);
		System.out.println("num1 = " + num1 + " num2 = " + num2);
	}
	public static void test10(int num1,int num2) {
		int temp = num1;
		num1 = num2;
		num2 = temp;
		System.out.println("num1 = " + num1 + " num2 = " + num2);
	}

输出:

num1 = 4 num2 = 3
num1 = 3 num2 = 4

说明:
第一行的输出是test10函数的打印,
第二行的输出是main函数的打印.
这个实例说明,test10函数不能改变实参的值它改变的是形式参数的值.

3.7 方法的重载(OverLoad)

3.7.1基本介绍

java中允许同一个类中,多个同名方法的存在,但要求 形参列表不一致!
比如:System.out.println(); out是PrintStream类型,这个函数可以打印,int,String,double…就是因为该函数使用了重载.

3.7.2 重载的好处

1)减轻了起名的麻烦
2)减轻了记名的麻烦

3.7.3重载的快速入门案例

看一个案例
【要求:类名MyPrint,方法名为print,可以打印 等腰三角形,矩形,立方体。示意】

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

		//当我们调用方法时,编译器会通过方法名+传入的实参, 来区别到底调用的是哪个方法
		MyPrint mp = new MyPrint();
		mp.print(10); //打印 等边三角形
		mp.print(10, 20); //打印 矩形
		mp.print(10, 20, 30); //打印立方体
	}
}

//要求:类名MyPrint,方法名为print,可以打印 等边三角形,矩形,立方体。示意
class MyPrint {
	//打印 等边三角形
	public void print(int w) {
		System.out.println("打印 等边三角形");
	}
	
	public void print(int len, int width) {
		System.out.println("打印 矩形");
	}

	//立方体
	public void print(int len, int width,int height) {
		System.out.println("打印立方体");
	}
}
3.7.4 注意事项和使用细节

1)方法名 :必须相同
2)参数列表:必须不同(参数类型或个数或顺序,至少有一样不同,参数名无要求)
3)返回类型:无要求

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

		MyCalculator mc = new MyCalculator();
		mc.calculate(1,2,3);
	}
}

//案例:类:MyCalculator  方法:calculate  
class MyCalculator { //小小计算器
	public double calculate(int n1, int n2) {
		return n1 + n2;
	}

	//1. 该方法和前面的方法,个数相同,但是类型不种
	public double calculate(int n1, double n2) {
		return n1 + n2;
	}

	//2.该方法和前面的方法,个数相同,类型相同,但是顺序不一样.
	public double calculate(double n2, int n1) {
		return n1 + n2;
	}

	//3.该方法和前面的方法,个数不相同
	public double calculate(int n1, int n2,int n3) {
		return n1 + n2 + n3;
	}

	//如果只是返回类型不一样,则不构成重载.
	/*
	public void calculate(int n1, int n2,int n3) {
		//return n1 + n2 + n3;
	}*/

	//如果只是形参的名字不同,则不构成重载.
	/*
	public double calculate(int a, int b,int c) {
		return a + b+ c;
	}*/
}

3.8 可变参数

3.8.1 基本概念

jdk5.0出现的新特性,java提供了一种机制,可以允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。

3.8.2 基本语法
访问修饰符 返回类型 方法名(数据类型... 形参名){
}
3.8.3 快速入门案例

看一个案例 类 MethodUtils,方法 sum 【可以计算 2个数的和,3个数的和 , 4. 5, 。。】 utils 工具集

public class VarParamterDemo { //可变参数的案例
    public static void main(String args[]) { 

		MethodUtils mu = new MethodUtils();
		System.out.println("mu.sum(1,2,3)= " + mu.sum(1,2,3));
		System.out.println("mu.sum(1,2,3,4)= " + mu.sum(1,2,3,4));
	}
}


//看一个案例 类 MethodUtils,方法 sum  【可以计算 2个数的和,3个数的和 , 4. 5, 。。】 

class MethodUtils {
	//传统的方法.
	/*public int sum(int n1, int n2) {
	}

	public int sum(int n1, int n2,int n3) {
	}*/
	//...传统的方法,即使用重载也很麻烦--》可变参数

	//说明
	//1. int...  表示 有0到多个 int 类型的参数
	//2. args 本质就是一个数组
	public int sum(int ... args) {
		int res = 0;
		for (int i = 0; i < args.length ; i++) {
			res += args[i];
		}
		return res;
	}
}
3.8.4 注意事项和使用细节

1)可变参数的实参可以为0个或任意多个。
2)可变参数的实参可以为数组。
3)可变参数的本质就是数组.
4)可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
5)一个形参列表中只可能出现一个可变参数

3.9 创建Java自定义类步骤总结

3.9.1 步骤

1)定义类,确定类名
2)编写类的属性
3)编写类的方法
4)创建对象,使用方法。

3.10 属性和局部变量作用域

3.10.1 基本使用

面向对象中,变量作用域是非常重要知识点,相对来说不是特别好理解,请大家注意听,认真思考,要求深刻掌握变量作用域。
1)在java编程中,主要的变量就是属性(成员变量,全局变量)和局部变量。A类:sum
2)我们说的局部变量一般是指在成员方法中定义的变量。
3)java中作用域的分类
全局变量:也就是属性,作用域为整个类体 A类:sum sub 等方法使用属性
局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中!
4)全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值。

3.10.2 注意事项和细节使用

1)属性全局变量和局部变量可以重名,访问时遵循就近原则。
2)在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
3)全局变量,存储在堆中。局部变量,存储在栈中,需要画一个示意图说明。【重要】
4)全局变量,生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡。局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡。即在一次方法调用过程中。【每调用一次方法,就开一个方法栈,这个是由操作系统决定的】
5)作用域不同
全局变量:可以被本类使用,或其他类使用(通过对象调用)
局部变量:只能在本类中对应的方法中的作用域内使用
6)修饰符不同
全局变量可以加修饰符
局部变量不可以加修饰符

4 构造方法

4.1 看一个需求

我们来看一个需求:前面我们在创建人类的对象时,是先把一个对象创建好后,再给他的年龄和姓名属性赋值,如果现在我要求,在创建人类的对象时,就直接指定这个对象的年龄和姓名,该怎么做? 这时就可以使用构造方法/构造器。

4.2 基本语法

[修饰符] 方法名(参数列表){
	构造方法体
}

说明:
1)构造器的修饰符可以默认
2)构造器没有返回值
3)方法名 和类名字必须一样
4)参数列表 和 成员方法一样的规则
5)构造器的调用有系统JVM 来调用

4.3基本介绍

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

4.4 快速入门

现在我们就用构造方法来完成刚才提出的问题:在创建人类的对象时,就直接指定这个对象的年龄和姓名,该怎么做?

public class ConstructorDemo { // 接收用户输入,并显示结果
	public static void main(String[] args){
		Person p = new Person("kristina", (short)20);
		System.out.println("p.name=" + p.name + " p.age=" + p.age);
	
	}
}

class Person {
	public String name;
	public short age;
	//构造器
	public Person(String pName, short pAge) {
		System.out.println("构造器 被调用");
		name = pName;
		age = pAge;
	}
}

4.5 注意事项和使用细节

1)一个类可以定义多个不同的构造方法,构造方法重载
比如:我们可以再给Person类定义一个构造方法,用该方法来创建新对象的时候,只指定人名,不需要指定年龄。
2)构造方法名和类名相同
3)构造方法没有返回值
4)主要作用是完成对新对象的初始化
5)在创建新对象时,系统自动的调用该类的构造方法
6)如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法(也叫默认构造方法),比如 Person (){}
7)一旦定义了自己的构造方法,默认的构造方法就覆盖了,就不能再使用默认的无参构造方法,除非显示的定义一下,即: Person(){};

public class ConstructorDemo { // 接收用户输入,并显示结果
	public static void main(String[] args){
		Person p = new Person("kristina", (short)20);
		System.out.println("p.name=" + p.name + " p.age=" + p.age);

		Person p2 = new Person("kristina");
		System.out.println("p2.name=" + p2.name + " p2.age=" + p2.age);

		A a = new A(); //默认构造器
	
	}
}

class Person {
	public String name;
	public short age;
	//构造器
	public Person(String pName, short pAge) {
		System.out.println("构造器 被调用");
		name = pName;
		age = pAge;
	}

	public Person(String pName) {
		System.out.println("构造器 被调用~");
		name = pName;
	}
}

class A {
	//如果你没有写任何构造器,会默认生成一个构造器,
	//默认构造器的无参
	public A() {
		System.out.println("A() 构造器");
	}

	//一旦定义了自己的构造方法,默认的构造方法就覆盖了,
	//就不能再使用默认的无参构造方法,除非显示的定义一下
	public A(String name) { //自己的构造方法
		
	}
}

4.6 对象创建的流程分析

class Person{
  int age=90;
  String name;
  Person(String n,int a){
         name=n;
         age=a;
  }}
Person p=new Person("小倩",20)

流程分析
1)JVM 机会加载类结构信息(Person.class)
2)在堆中分配空间(地址 )
3)对属性进行初始化
3.1 默认初始化 age = 0 name = null
3.2 显示初始化 age = 90 name = null
3.3 使用构造器进行初始化 age = 20 name = “小倩”
4) 将 堆中的对象的地址,返回给p

5 this关键字

简单的说,哪个对象调用,this就代表哪个对象

public class ThisDemo2 { // 接收用户输入,并显示结果
	public static void main(String[] args){

		Cat cat1 = new Cat("波斯猫", (byte)2);
		System.out.println("cat1.hashCode=" + cat1.hashCode());
		cat1.showInfo();
		
		
		Cat cat2 = new Cat("布偶猫", (byte)1);
		System.out.println("cat2.hashCode=" + cat2.hashCode());
		cat2.showInfo();
	}
}

class Cat {
	public String name;
	public byte age;
	public Cat(String name, byte age) {
		this.name = name;
		this.age = age;
	}

	//显示信息
	public void showInfo() {
		//this : 那个对象调用showInfo方法,this就指向该对象

		System.out.println("信息= " + this.name + " age=" + this.age + "this地址=" + this.hashCode());
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值