5.面向对象
5.0类与对象
案例:
张老太养了两只猫:一只名字叫小白,今年 3 岁,白色。还有一只叫小花,今年 100 岁,花色。请编写一个程序,输出两只猫的信息。
public class Object1 {
public static void main(String[] args) {
//创建Cat的对象
Cat cat1 =new Cat();
//对象初始化
cat1.name="小白";
cat1.age=3;
cat1.color="白色";
//创建Cat的对象
Cat cat2 =new Cat();
//对象初始化
cat2.name="小花";
cat2.age=100;
cat2.color="花色";
//访问对象属性
System.out.println("第1只猫信息:"+cat1.name+"\t"+cat1.age+"\t"+cat1.color);
System.out.println("第2只猫信息:"+cat2.name+"\t"+cat2.age+"\t"+cat2.color);
}
}
//创建Cat类
class Cat{
//属性
String name;
int age;
String color;
}
如何创建对象:
1) 先声明再创建
Cat cat ; //声明对象
cat cat = new Cat(); //创建
2) 直接创建
Cat cat = new Cat();
类与对象的关系示意图:
类和对象的区别和联系:
类是抽象的,代表一类事物,比如人类、猫类等。
对象是具体的,代表一个个具体事物, 即实例。
类是对象的模板,对象是类的一个个体,对应一个个实例。
对象在内存中存在形式:
类和对象的内存分配机制:
栈: 一般存放基本数据类型(局部变量)
堆: 存放对象(Cat、数组等)
方法区:常量池(常量,比如字符串), 类的加载信息
创建对象的流程:
Cat cat =new Cat();
//对象初始化
cat.name="小白";
cat.age=3;
- 加载Cat类的信息(属性和方法),只会加载一次
- 在堆中给对象分配空间,进行默认初始化
- 把对象的地址赋给cat,cat指向堆中的对象
- 进行指定初始化,如:
cat.name="小白";
5.1属性
-
概念:常见叫法还有: 成员变量 、字段(field)
-
属性是类的一个组成部分,一般是基本数据类型,也可是引用数据类型。
-
注意事项和细节:
①属性的定义语法同变量一样,示例:访问修饰符 属性类型 属性名;
访问修饰符: 控制属性的访问范围。有四种访问修饰符 public, proctected, 默认, private
②属性如果不赋值,有默认值。
int 、short、byte 、long: 0
float 、double :0.0
char: \u0000
boolean: false
String: null
5.2方法
5.2.1介绍
在某些情况下,我们要需要定义成员方法(简称方法)。比如人类:除了有一些属性外( 年龄,姓名…),我们人类还有一些行为比如:可以说话、跑步…,通过学习,还可以做算术题。这时就要用成员方法才能完成。
class Person{
//属性
String name;
int age;
//方法
public void speak(){
System.out.println("我是一个好人");
}
}
/*
public void speak(){}:
public 表示方法是公开
void 表示方法没有返回值
speak 是方法名, () 里是形参列表
{} 里是方法体
*/
方法的调用机制原理:
方法的定义:
访问修饰符 返回数据类型 方法名(形参列表..) {//方法体语句;
return 返回值;
}
方法注意事项和使用细节:
访问修饰符 (作用是控制 方法使用的范围),如果不写为默认访问
返回数据类型:
- 一个方法最多有一个返回值 [思考,如何返回多个结果 :返回数组 ]
- 返回值可以是任何类型,包含基本类型和引用类型
- 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值;而且要求返回值类型必须和 return 的值类型一致或兼容
- 如果方法是 void,则方法体中可以没有 return 语句,或者 只写
return ;
方法名:遵循驼峰命名法,见名知义
5.2.2方法传参机制
- 基本数据类型的传参机制
值传递:形参的改变不影响实参。
- 引用数据类型的传参机制
引用类型传递的是地址,可以通过形参影响实参。
5.3方法递归调用
1.基本介绍: 递归就是方法自己调用自己,每次调用时传入不同的变量。
2.递归能解决什么问题?
//阶乘
public int factorial(int n) {
if (n == 1) {
return 1;
} else {
return factorial(n - 1) * n;
}
}
3.递归重要规则
//求斐波那契数 1,1,2,3,5,8,13...
public int fibonacci(int n) {
if( n >= 1){
if( n == 1 || n == 2) {
return 1;
} else {
return fibonacci(n-1) + fibonacci(n-2);
}
} else {
System.out.println("要求输入的 n>=1 的整数");
return -1;
}
}
5.4 方法重载(OverLoad)
-
基本介绍:java允许同一个类中,多个同名方法的存在,但要求形参列表不一致!
-
重载的好处: 减轻了起名和记名的麻烦。利于接口编程
-
注意事项和使用细节:
方法名:必须相同
形参列表:必须不同(形参类型、个数或顺序至少有一个不同,形参名无要求)
返回类型:无要求
class MyCalculator{
//下面的四个 calculate方法构成了重载
//方法1
public int calculate(int n1, int n2) {
System.out.println("方法1被调用...");
return n1 + n2;
}
//方法2
public double calculate(int n1, double n2) {
System.out.println("方法2被调用...");
return n1 + n2;
}
//方法3
public double calculate(double n1, int n2) {
System.out.println("方法3被调用...");
return n1 + n2;
}
//方法4
public int calculate(int n1, int n2,int n3) {
System.out.println("方法4被调用...");
return n1 + n2 + n2;
}
}
5.5可变参数
基本概念:
java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法,可以通过可变参数实现。
基本语法:
访问修饰符 返回类型 方法名(数据类型... 形参名) {
}
//案例 类 HspMethod中,方法 sum 可以计算 2 个数的和,3 个数的和 ,...)
class HspMethod {
public int sum(int... nums) {
//System.out.println("接收的参数个数=" + nums.length);
int res = 0;
for(int i = 0; i <= nums.length; i++) {
res += nums[i];
}
return res;
}
//1. int... 表示接受的是可变参数,类型是 int ,即可以接收多个 int(0-多)
//2. 使用可变参数时,可以当做数组来使用 即 nums 可以当做数组
//3. 可变参数的本质就是数组
//4. 可变参数可以和普通参数类型放在一起,但可变参数要放到最后
//5. 一个参数列表中只能有一个可变参数
5.6作用域
概念:
1.java中主要的变量是属性和局部变量
2.局部变量一般是在方法中定义的变量
3.java作用域的分类
- 全局变量:
- 即属性,作用域为整个类
- 可以被本类或其他类使用
- 前面可以加修饰符
- 局部变量:
- 除属性之外的其他变量,作用域为定义它的代码块中
- 只能在本类对应的方法中使用
- 前面不能加修饰符
4.全局变量可以不赋值,直接使用(有默认值);局部变量必须赋值后才能使用(没有默认值)
注意事项和使用细节:
1.局部变量和属性可以重名,访问时遵循就近原则
2.同一个作用域中,例如同一个方法中,两个局部变量不能重名
3.属性生命周期较长,伴随对象创建而创建,销毁而销毁;
局部变量生命周期较短,伴随执行代码块而创建,代码块执行结束就销毁。
String name = "jack";
public void say() {
//细节 属性和局部变量可以重名,访问时遵循就近原则
String name = "king";
System.out.println("say() name=" + name);
}
5.7构造器
前面在创建人类的对象时,是先把一个对象创建好后,再给他的年龄和姓名属性赋值;如果现在要求在创建人类的对象时,就直接指定这个对象的年龄和姓名,该怎么做? 这时就可以使用构造器。
基本介绍
基本语法
[修饰符] 方法名(形参列表){
方法体;
}
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。
它有几个特点:
- 方法名和类名相同
- 无返回值
- 在创建对象时,系统会自动的调用该类的构造器完成对象属性的初始化
- 构造器是完成对象的初始化,而不是创建对象
- 参数列表 和 方法的规则一样
- 一个类可以定义多个不同的构造器,即构造器重载
- 如果没有人为定义构造器,系统会自动生成一个默认的无参构造器。
- 一旦定义了自己的构造器,默认构造器就被覆盖了,除非显示定义一下。
public class Constructor {
public static void main(String[] args) {
Dog m=new Dog("大黄",4);
System.out.println(m.name);
Dog n=new Dog("小黑");
System.out.println(n.name);
}
}
class Dog{
String name;
int age;
//构造器
public Dog(String pName,int pAge){
name=pName;
age=pAge;
}
//构造器重载
public Dog(String pName){
name=pName;
}
class Test{
// 默认无参构造器,这里显示定义
Test(){
}
}
注意事项和使用细节:
5.8this关键字
this概念:java虚拟机给每个对象分配this,代表当前对象。
public class This01 {
public static void main(String[] args) {
Dog dog1 = new Dog("大壮", 3);
System.out.println("dog1的hashcode=" + dog1.hashCode());
dog1.info();
//
Dog dog2 = new Dog("大黄", 2);
System.out.println("dog2的hashcode=" + dog2.hashCode());
dog2.info();
}
}
class Dog{ //类
String name;
int age;
// public Dog(String dName, int dAge){//构造器
// name = dName;
// age = dAge;
// }
//如果构造器的形参能够直接写成属性名,就更好了
//但是出现了一个问题,根据变量的作用域原则,构造器的name和age是局部变量,而不是属性
// 引出this关键字来解决
public Dog(String name, int age){//构造器
//访问本类的属性
this.name = name;//this.name就是当前对象的属性name
this.age = age; //this.age就是当前对象的属性age
System.out.println("this.hashCode=" + this.hashCode());
}
public void info(){//成员方法,输出属性信息
System.out.println(name + "\t" + age + "\t");
}
}
this 的注意事项和使用细节:
- this 关键字可以用来访问本类的属性、方法、构造器
- this 用于区分当前类的属性和局部变量
- 访问方法的语法:
this.方法名(参数列表);
- 访问构造器语法:
this(参数列表);
注意只能在构造器中访问另外一个构造器, 必须放在第一条语句。 - this 不能在类定义的外部使用,只能在类定义的方法中使用。
public class ThisDetails {
public static void main(String[] args) {
T t1 = new T();
t1.f2();
}
}
class T {
public T() {//构造器
this(15);//在构造器中调用另一个构造器
}
public T(int n) {
System.out.println("有参构造器被调用");
}
public void f1() {
System.out.println("方法1被调用");
}
public void f2() {
//调用f1方法
f1();//方式1
this.f1(); //方式2
}
}