类与对象
-
看一个养猫问题 张老太养了两只猫:一只名字叫小白,今年三岁,白色。还有一只叫小花,今年一百岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示张老太没有这只猫猫
-
使用现有的技术解决
/** * @author L.yn * @version 1.0 * @date 2021/12/12 14:55 */ public class Object01 { public static void main(String[] args) { /*张老太养了两只猫: 一只名字叫小白,今年三岁,白色。 还有一只叫小花,今年一百岁,花色。 请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。 如果用户输入的小猫名错误,则显示张老太没有这只猫猫*/ //一、单独变量来解决 => 不利于数据的管理(把一只猫的信息拆解) //第一只猫信息 String cat1Name = "小白"; String cat1Color = "白色"; int cat1Age = 3; //第二只猫信息 String cat2Name = "小花"; String cat2Color = "花色"; int cat2Age = 100; //二、数组 => (1)数据类型体现不出来 // (2)只能通过[下标]获取信息,造成变量名字和内容的对应关系不明确 // (3) 不能体现猫的行为 String[] cat1 = {"小白", "3", "白色"}; String[] cat2 = {"小花", "100", "花色"}; } }- 现有技术解决的缺点分析
- 不利于数据的管理
- 效率低======>引出我们的新知识:类与对象(OOP)
- 现有技术解决的缺点分析
-
-
一个程序就是一个对象,有很多事务(对象[属性,行为])
-
类与对象的关系示意图
- 对上图说明
- 类就是数据类型,比如Cat
- 对象就是一个具体的实例
- 对上图说明
-
快速入门-面向对象的方式解决养猫问题
/** * @author L.yn * @version 1.0 * @date 2021/12/12 14:55 */ public class Object01 { public static void main(String[] args) { /*张老太养了两只猫: 一只名字叫小白,今年三岁,白色。 还有一只叫小花,今年一百岁,花色。 请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。 如果用户输入的小猫名错误,则显示张老太没有这只猫猫*/ //使用oop面向对象解决 //实例化一只猫(创建一只猫对象) /** * 一、new Cat();创建一只猫 * 二、Cat cat1 = new Cat(); 把创建的猫赋给cat1 * 三、cat1、cat2就是一个对象 */ Cat cat1 = new Cat(); cat1.name = "小白"; cat1.age = 3; cat1.color = "白色"; Cat cat2 = new Cat(); cat2.name = "小花"; cat2.age = 100; cat2.color = "花色"; //怎么访问对象的属性呢 //第一只猫信息:小白 3 白色 //第二只猫信息:小花 100 花色 System.out.println("第一只猫信息:" + cat1.name + " " + cat1.age + " " + cat1.color); System.out.println("第二只猫信息:" + cat2.name + " " + cat2.age + " " + cat2.color); } } /** * 使用面向对象的方式来解决养猫问题 * 字定义一个猫类 Cat -> 字定义的数据类型 */ class Cat { //属性 String name;//名字 int age;//年龄 String color;//颜色 } -
对象在内存中存在的形式
-
属性/成员变量
-
基本介绍
-
从概念或叫法上看:成员变量 = 属性 = field(即 成员变量是用来表示属性的)
-
属性时类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象,数组)
-
class Car { String name;//属性,成员变量,字段 field double price; String color; String[] master;//属性可以是基本数据类型,也可以是引用数据类型(对象,数组) }
-
-
注意事项和细节说明
- 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
- 访问修饰符:控制属性的访问范围,有四种访问修饰符:public proctected default private
- 属性的定义类型可以为任意类型,包含基本类型和引用类型
- 属性如果不赋值,有默认值,规则和数组一致
- 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
-
-
-
如何创建对象
- 先声明在创建 Cat cat;//声明对象 cat = new Cat();//创建对象
- 直接创建 Cat cat = new Cat();
-
如何访问属性
- 基本语法
- 对象名.属性名 cat.name; cat.age; cat.color;
- 基本语法
-
类与对象的内存分配机制
- java内存的结构分析
- 栈:一般存放基本数据类型(局部变量)
- 堆:存放对象(Cat cat,数组等)
- 方法区:常量池(常量,比如字符串),类加载信息
- java创建对象的流程简单分析
Person p = new Person();
p.name = "jack";
p.age = 10;
- 先加载Person类信息(属性和方法信息,只会加载一次)
- 在堆中分配空间,进行默认初始化(看规则)
- 把地址赋值给p,p就指向对象
- 进行指定初始化
- java内存的结构分析
成员方法
基本介绍
在某些情况下,我们要需要定义成员方法(简称方法)。比如人类:除了有一些属性外(年龄,姓名..),我们人类还有一些行为,比如:可以说话,跑步...,通过学习,还可以做算术题,这时就要用成员方法才能完成
-
成员方法快速入门
-
一:添加speak成员方法,输出我是一个好人 二:添加cal01方法,可以计算从1+...+100的结果 三:添加getSum成员方法,可以计算两个数的和
-
/** * @author L.yn * @version 1.0 * @date 2021/12/12 16:45 */ public class Method01 { public static void main(String[] args) { //方法使用 //1、方法写好后,如果不去调用,不会生效 Person person = new Person(); //speak方法输出我是一个好人 //al01方法输出:5050 //getSum方法输出:3 person.speak(); person.al01(); System.out.println(person.getSum(1, 2)); } } /** * 一:添加speak成员方法,输出我是一个好人 * 二:添加cal01方法,可以计算从1+...+100的结果 * 三:添加getSum成员方法,可以计算两个数的和 */ class Person { String name; int age; public void speak() { System.out.println("speak方法输出我是一个好人"); } public void al01() { int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } System.out.println("al01方法输出:" + sum); } public String getSum(int num1, int num2) { return "getSum方法输出:" + (num1 + num2); } }
-
方法递归调用
基本介绍
简单的说:递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂问题,同时可以让代码变得更简洁
递归能解决什么问题?
- 各种数学问题:汉诺塔,阶乘问题,迷宫问题等
- 各种算法中也会使用到递归:比如快排,归并排序,二分查找,分治算法等
- 将用栈解决的问题-->递归代码比较简洁
递归重要规则
- 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
- 方法的局部变量是独立的,不会相互影响,比如变量n
- 如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引用类型的数据
- 递归必须向退出递归的条件逼近,否则就是无限递归,出现stackoverflowerror
- 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕
递归练习
- 猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并在多吃了一个!以后每天猴子都吃其中的一半,然后在多吃一个,当到第十天时,想再吃时(即还没吃),发现只有一个桃子了,问题:最初共多少个桃子
方法重载(OverLoad)
-
快速入门案例
package com.demo.a; /** * @author L.yn * @version 1.0 * @date 2021/12/13 18:22 */ public class OverLoadDemo1 { public static void main(String[] args) { //使用方法 System.out.println(new MyCalculator().calculate(1, 2));//3 System.out.println(new MyCalculator().calculate(1, 2.2));//3.2 System.out.println(new MyCalculator().calculate(1.1, 2));//3.1 System.out.println(new MyCalculator().calculate(1, 2, 3));//6 } } /** * 下面的四个calculate方法构成了重载 */ class MyCalculator { //两个整数的和 int calculate(int n1, int n2) { return n1 + n2; } //一个整数,一个double的和 double calculate(int n1, double n2) { return n1 + n2; } //一个整数,一个double的和 double calculate(double n1, int n2) { return n1 + n2; } //三个整数的和 int calculate(int n1, int n2, int n3) { return n1 + n2 + n3; } } -
注意事项和使用细节
- 方法名:必须相同
- 形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
- 返回类型:无要求
-
练习
-
编写程序,类Methods中定义三个重载方法并调用。方法名字定义,参数字定义,在main方法中调用三个方法
-
可变参数
基本概念
java允许将同一个类中多个同名通功能但参数个数不同的方法,封装成一个方法
基本语法
访问修饰符 返回类型 方法名(数据类型... 形参名){}
快速入门案例:可以计算2个数的和,3个数的和,4,5....
package com.demo.a;
/**
* @author L.yn
* @version 1.0
* @date 2021/12/15 19:14
*/
public class VarParameterDemo {
public static void main(String[] args) {
System.out.println(sum(1, 2, 3));//6
System.out.println(sum());//0
System.out.println(sum(123, 4134));//4257
}
/**
* int... 表示接收的是可变参数,类型是int,即可以接收多个int(0->多)
* 使用可变参数时,可以当作数组来使用
* 遍历nums求和即可
*
* @param nums
* @return
*/
public static int sum(int... nums) {
int sum = 0;
for (int num : nums) {
sum += num;
}
return sum;
}
}
注意事项
- 可变参数的实参可以为0个或任意多个
- 可变参数的实参可以为数组
- 可变参数的本质就是数组
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
- 一个形参列表中只能出现一个可变参数 public int sum(int... a, String... b)//错误
作用域
基本使用
面向对象中,变量作用域是非常重要知识点,相对来说不是特别好理解
- 在java编程中,主要的变量就是属性(成员变量)和局部变量
- 我们说的局部变量一般是指在成员方法中定义的变量
- java中作用域的分类
- 全局变量:也就是属性,作用域为整个类体
- 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中
- 全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值
- 注意事项和细节
- 属性和局部变量可以重名,访问时遵循就近原则
- 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名
- 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡,局部变量,生命周期较短,伴随着它的代码块执行而创建,伴随着代码块的结束而死亡,即在一次方法调用过程中
- 作用域范围不同
- 全局变量:可以被本类使用,或其他类使用(通过对象调用)
- 局部变量:只能在本类中对应的方法中使用
- 修饰符不同
- 全局变量/属性可以加修饰符
- 局部变量不能加修饰符
构造方法/构造器
- 看一个需求
前面我们创建人类的对象时,是先把一个对象创建好后,在给他的年龄和姓名属性赋值,如果现在我要求,在创建人类的对象时,就直接指定这个对象的年龄和姓名,该怎么做?这时就可以使用构造器
- 基本语法
[修饰符] 方法名(形参列表){
方法体
}
public class Person{
String name;
int age;
//构造器
/**
*一、构造器没有返回值,也不能写void
*二、构造器的名称和类Person一样
*/
public Person(String name,int age){
this.name = name;
this.age = age;
}
}
- 构造器的修饰符可以默认
- 构造器没有返回值
- 方法名和类名字必须一样
- 参数列表和成员方法一样的规则
- 构造器的调用系统完成
- 基本介绍
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化,它有几个特点
- 方法名和类名相同
- 没有返回值
- 构造器时完成对象的初始化,而不是创建对象
- 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化
- 如果程序员没有定义构造方法,系统会自动给类生成一个默认午餐构造方法,比如Person(){} ,使用javap指令反编译看看
- 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能使用默认的午餐构造器,除非显示的定义一下,即:Person(){}
对象创建流程分析
- 加载Person类信息,只会加载一次
- 在堆中分配空间(地址)
- 完成对象初始化[3.1默认初始化,age=0 name=null]
this关键字
package com.demo.a;
/**
* @author L.yn
* @version 1.0
* @date 2021/12/19 15:51
*/
public class ThisDemo {
public static void main(String[] args) {
Dog dog = new Dog("大壮", 18);
dog.info();//大壮 18
}
}
class Dog {
String name;
int age;
// public Dog(String dName, int dAge) {//构造器
// name = dName;
// age = dAge;
// }
//如果我们构造器的形参,能够直接写成属性名,就更好了
/**
* 但是出现了一个问题,根据变量的作用域原则
* 构造器的name是局部变量,而不是属性
* 构造器的age是局部变量,而不是属性
* ==> 引出this关键字来解决
*
* @param name
* @param age
*/
public Dog(String name, int age) {//构造器
name = name;
age = age;
}
public void info() {//成员方法,输出属性X信息
System.out.println(name + " " + age);
}
}
- 什么是this
java虚拟机会给每个对象分配this,代表当前对象。坦白的讲,要明白this不是件容易的事
-
使用this解决前面变量命名的问题
package com.demo.a; /** * @author L.yn * @version 1.0 * @date 2021/12/19 15:51 */ public class ThisDemo { public static void main(String[] args) { Dog dog = new Dog("大壮", 18); dog.info();//大壮 18 } } class Dog { String name; int age; // public Dog(String dName, int dAge) {//构造器 // name = dName; // age = dAge; // } //如果我们构造器的形参,能够直接写成属性名,就更好了 /** * 但是出现了一个问题,根据变量的作用域原则 * 构造器的name是局部变量,而不是属性 * 构造器的age是局部变量,而不是属性 * ==> 引出this关键字来解决 * * @param name * @param age */ // public Dog(String name, int age) {//构造器 // name = name; // age = age; // } public Dog(String name, int age) {//构造器 //this.name 就是当前对象的属性 this.name = name; //this.age 就是当前对象的属性 this.age = age; } public void info() {//成员方法,输出属性X信息 System.out.println(name + " " + age); } } -
小结:简单的说,哪个对象调用,this就代表哪个对象
-
注意事项和使用细节
-
this关键字可以用来访问本类的属性,方法,构造器
-
this用于区分当前类的属性和局部变量
-
访问成员方法的语法:this.方法名(参数列表)
-
访问构造器语法:this(参数列表);注意只能在构造器中使用
-
this不能在类定义的外部使用,只能在类定义的方法中使用
-
package com.demo.a; /** * @author L.yn * @version 1.0 * @date 2021/12/19 16:24 */ public class ThisDemo1 { public static void main(String[] args) { //T(String name, int age)构造器 //f2方法 //f1方法 //f1方法 new T().f2(); } } class T { String name; int age; //访问构造器语法:this(参数列表);注意只能在构造器中使用 //注意:访问构造器语法:this(参数列表)必须放置在第一条语句 public T() { this("aa", 19); } public T(String name, int age) { System.out.println("T(String name, int age)构造器"); } //访问成员方法的语法:this.方法名(参数列表) public void f1() { System.out.println("f1方法"); } public void f2() { System.out.println("f2方法"); //第一种方式 f1(); //第二种方式 this.f1(); } }