1、单独用变量来解决:不利于数据的管理, 把猫的三个属性拆开了。
2、使用数组的方式解决:
-
数据类型体现不出来,由于数组中的类型是一致的,所有不能体现出不同的数据类型。
-
只能通过下标的方式获取信息,这样会造成变量名字和数据内容的对应关系不明确
-
不能体现出猫的行为。即对象的行为。
所以,引入类与对象
public class Object01{
public static void main(String[] args){
/*
使用OOP面向对象解决问题
1、实例化一只猫【创建一只猫对象】
2、Cat cat1 = new Cat(); 把创建的猫赋给 cat1
cat1是对象名(对象引用)
new Cat() 创建的对象空间(数据),才是真正的对象。
*/
Cat cat1 = new Cat();
cat1.name = "小白";
cat1.age = 10;
cat1.color = "白色";
Cat cat2 = new Cat();
cat2.name = "小花";
cat2.age = 100;
cat2.color = "花色";
// 怎么访问属性呢?
System.out.println("第一只猫的信息:" + cat1.name + "年龄: " + cat1.age);
}
}
// 使用面向对象的方式解决养猫问题
// 定义一个猫类, Cat -> 自定义数据类型
class Cat{
// 属性
String name;
int age;
String color;
// 行为
}
类与对象
类在java中就是数据类型(相当于int),比如Cat
对象就是一个具体的实例。 (int a = 100;其实相当于实例化,100就是个对象)
总结:
1、类是抽象的,概念的,代表一类事物,比如人类,猫类…,即它是数据类型
2、对象是具体的,实际的,代表一个具体事物,即实例。
3、类是对象的模版,对象是类的一个 个体,对应一个实例。
对象在内存中的存在形式
堆和方法区中保存的为对象,栈中保存的为对象的引用。
属性【成员变量、字段】概念
基本介绍
1、从概念或叫法上:成员变量 = 属性 = field(字段) (即,成员变量是用来表示属性的。)
2、属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象、数组)。
注意事项和细节说明
1、属性的定义语法同变量。示例: 访问修饰符 属性类型 属性名;
访问修饰符的作用为:控制属性的访问范围: 有四种,public,proctected,默认,private
2、属性的定义类型可以为任意类型,包含基本类型或引用类型。
3、属性如果不赋值,则有默认值,规则和数组一致。
int、short、byte、long: 0
boolean: flase
String:null
float、double: 0.0
char:\u0000
如何创建对象
1、先声明再创建
Cat cat; //声明对象cat,此时没有分配空间。
cat = new Cat(); // 创建对象,在堆中创建对象,并把地址赋给cat对象。
2、直接创建
Cat cat = new Cat();
类和对象的内存分配机制
0198_韩顺平Java_对象分配机制_哔哩哔哩_bilibili
java内存的结构分析:
1、栈:一般存放基本数据类型(局部变量)
2、堆:存放对象(Cat cat,数组等等信息)
3、方法区: 常量池(常量,比如字符串)类加载信息
// 1、先加载Persion类信息(属性和方法信息,只加载一次,之后就不会重新加载了)
// 2、在堆中分配空间,对属性进行默认初始化,并把地址赋给p。
Persion p = new Persion();
// 3、对属性进行赋值。 p.age = 10;
p.name = "jack";
p.age = 10;
测试题【重要】
成员方法【行为】
基本介绍
在某些情况下,我们需要定义成员方法(简称方法),比如:人类,除了有一些属性外,还包括一些行为。
public class Object01{
public static void main(String[] args){
Person p = new Person();
// 方法写好后,如果不去调用,则不输出。
p.speak(); // 调用方法。
}
}
// 使用面向对象的方式解决养猫问题
// 定义一个猫类, Cat -> 自定义数据类型
class Person{
// 属性
String name;
int age;
// 方法(行为)
/*
public: 表示方法是公开的
void:表示方法没有返回值
speak(): 方法名,()形参列表
{}: 方法体,要实现的行为
*/
public void speak(){
System.out.println("我是一个好人");
}
}
成员方法传参机制
0203_韩顺平Java_方法调用机制_哔哩哔哩_bilibili
测试题
1、请遍历一个数组,输出数组的各个元素值。
public class Object01{
public static void main(String[] args){
int[][] map = {{0,0,1}, {1,1,1},{1,1,3}};
GetData tools = new GetData();
tools.getData(map);
}
}
class GetData{
public void getData(int[][] map){
for (int i=0; i<map.length; i++) {
for (int j=0; j<map[i].length; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}
}
成员方法的好处:
1、提高代码的复用性
2、可以将实现的细节封装起来,然后供其他用户来调用即可。
成员方法的定义
访问修饰符 返回数据类型 方法名(形参列表 ...) {
方法体;
return 返回值;
}
1、形参列表:用来表示成员方法的输入信息
2、数据类型(返回类型):表示成员方法的返回值类型。void表示没有返回值。
3、方法主体: 表示为了实现某一功能的代码体
4、return 语句不是必须的。
注意事项和使用细节
方法定义
1、访问修饰符(作用是控制方法的使用范围),可选,有四种。
2、返回数据类型
-
一个方法最多有一个返回值【思考,如何返回多个结果,则返回数组】
-
返回类型可以为任意类型,包含基本类型或引用类型(数组,自定义的对象)
-
如果方法定义中要求有返回数据类型,则方法体中最后的执行语句必须为return,而且要求返回值类型必须和return的值类型一致或兼容。
-
如果方法为void,则方法体中可以没有return语句,或者只写return即可。
3、方法名命名规则为小驼峰命名法,最好见名知意。
类型兼容:
char < int < long < float < double
byte < short < int < long < float < double
byte,short,char 三者间进行运算时首先将类型转换为int。 自动提升原则,表达式结果的类型自动提升为操作数中的最大类型。
形参列表
1、一个方法可以有0个参数,也可以有多个参数,使用逗号隔开
2、参数类型可以为任意类型,包含基本类型或引用类型。
3、调用带参数的方法时,一定对应着参数列表传入相同类型或者兼容类型的参数。方法中定义的参数类型可以兼容传入的参数类型。
4、方法定义时的参数称为形式参数,简称形参。方法调用时的参数称为实际参数,简称实参。
实参和形参的类型要一致或兼容、个数、顺序必须一致。
方法体【代码块】
实现具体功能的语句,可以为输入、输出、变量、运算、分支、循环、方法调用等等,但里面不能再定义方法!即:方法不能嵌套定义。
方法调用细节说明
1、同一个类中的方法调用:直接调用即可。
2、跨类中的方法A类调用B类方法:需要通过对象名调用。比如:对象名.方法名(参数)
特别说明:
跨类的方法调用和方法的访问修饰符相关。等之后讲到访问修饰符时,再细说。
测试题
编写类AA,然后定义一个方法:判断一个数是奇数odd还是偶数。返回Boolean
public class Object01{
public static void main(String[] args){
A a = new A();
String content= a.isOdd(10)? "是奇数": "是偶数";
System.out.println(content);
}
}
class A{
public boolean isOdd(int num){
// 方法一:
// if (num % 2 == 0){
// return true;
// } else{
// return false;
// }
// 方法二:
// return num %2 !=0 ? true : false;
// 方法三:
return num % 2 != 0;
}
}
成员方法的传参机制
基本数据类型的传参机制
public class Object01{
public static void main(String[] args){
int a = 10;
int b = 20;
A obj = new A();
obj.swap(a, b);
System.out.println("main方法 a = " + a + "\tb=" + b);
}
}
class A{
public void swap(int a, int b){
System.out.println("\na和b交换前的值\n a=" + a + "\tb=" + b);
int tmp = a;
a = b;
b = tmp;
System.out.println("\n a和b交换后的值 \n a=" + a + "\tb=" + b);
}
}
基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参。
引用数据类型的传参机制
public class Object01{
public static void main(String[] args){
int[] c = {10,20,30};
A obj = new A();
obj.change(c);
System.out.println("main方法 a = " + a + "\tb=" + b);
for (int i=0; i<c.length; i++) {
System.out.print(c[i] + "\t");
}
}
}
class A{
public void change(int[] a){
a[1] = a[0];
}
}
引用类型传递的是地址,可以通过修改形参来影响实参。
0211_韩顺平Java_方法传参机制2_哔哩哔哩_bilibili
递归(recursion)
基本介绍
递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂问题,同时使代码变得简洁。
能解决什么问题
1、各种数学问题如:8皇后问题,汉诺塔,阶乘问题,迷宫问题,球和篮子的问题
2、各种算法中也会使用到递归。如快排、归并排序、二分查找、分治算法等【之后细讲】
3、将用栈解决的问题使用递归解决则非常简单。
递归的调用机制
public class recursion{
public static void main(String[] args){
// 编写一个main方法
T t1 = new T();
t1.test(4);
}
}
class T{
// 分析
public void test(int n){
if (n>2){
test(n-1);
}
System.out.println("n=" + n);
}
}
递归重要规则
1、执行一个方法时,就会创建一个新的受保护的独立空间(栈空间)。
2、方法的局部变量是独立的,不会相互影响。 比如上面代码中的变量 n。
3、如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引用类型的数据。
4、递归必须向退出递归的条件靠近,否则就会出现无限递归的情况,出现StackOverflowError。
5、当一个方法执行完毕,或者遇到return时,就会返回,遵守谁调用,就将结果返回给谁的原则。
递归测试题
public class recursion{
public static void main(String[] args){
// 编写一个main方法
T t1 = new T();
int result = t1.test(7);
System.out.println(result);
}
}
class T{
// 分析
public int test(int n){
if (n <= 2){
return 1;
} else{
return test(n-1) + test(n-2);
}
}
}
public class HanoiTower{
public static void main(String[] args){
Tower tower = new Tower();
tower.move(2, 'A', 'B', 'C');
}
}
class Tower {
// num 表示要移动的个数,a、b、c分别表示A塔、B塔、C塔
public void move(int num, char a, char b, char c){
if (num == 1){
System.out.println(a + "->" + c);
} else {
/*
如果有多个盘,则可以将整体看成两个,分别为最下面的和上面的所有盘(num-1)
*/
// 1、先移动上面的所有盘到b,借助c塔
move(num-1, a, c, b);
// 2、把最下面的这个盘,移动到c
System.out.println(a + "->" + c);
// 3、再把B塔的所有盘,移动到C, 借助A
move(num-1, b, a, c);
}
}
}
OverLoad【重载】
基本介绍
java中允许同一个类中,多个同名方法的存在,但要求,形参列表不一致!
重载的好处
1、减轻了起名的麻烦
2、减轻了记名的麻烦
注意细节
-
方法名:必须相同
-
形参列表:必须不同( 形参类型或个数或顺序,至少有一样不同, 参数名无要求)
-
返回类型:对于返回类型无要求
测试
public class OverLoad01{
public static void main(String[] args){
int z = calculate(1,2);
System.out.println(z);
double r = calculate(1.3,2);
System.out.println(r);
}
// 重载对于返回值类型无要求,但对于形参是有要求的,包括,形参类型或个数或顺序,至少有一样不同
public static int calculate(int x, int y){
System.out.println("int x, int y");
return x+y;
}
public static double calculate(int x, double y){
System.out.println("double x, double y");
return x+y;
}
public static double calculate(double x, int y){
System.out.println("double x, int y");
return x+y;
}
public static int calculate(int x, int y, int z){
System.out.println("int x, int y, int z");
return x+y+z;
}
}
可变参数
基本概念
java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
即实参可以传入0个到多个参数
基本语法
访问修饰符 返回类型 方法名(数据类型... 形参名){
}
public class OverLoad01{
public static void main(String[] args){
Test t = new Test();
int d = t.VarParamer(1,2,3,4);
System.out.println("总和为" + d);
}
}
class Test{
public int VarParamer(int... x){
System.out.println("参数的个数为:" + x.length);
int sum = 0;
for (int i =0; i<x.length; i++) {
sum += x[i];
}
return sum;
}
}
可变参数的注意细节
-
可变参数的个数可以有0个或者多个。
-
可变参数的 实参 可以为数组。
-
可变参数的 本质 就是数组。
-
可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后。
-
一个形参列表中,只能出现一个可变参数。
public class OverLoad01{
public static void main(String[] args){
Test t = new Test();
int d = t.varParamer(new int[]{1,2,3});
System.out.println("总和为" + d);
}
}
class Test{
public int varParamer(int... x){
System.out.println("参数的个数为:" + x.length);
int sum = 0;
for (int i =0; i<x.length; i++) {
sum += x[i];
}
return sum;
}
}
作用域
基本使用
1、在java编程中,主要的变量就是全局变量(属性、成员变量) 和 局部变量
2、我们说的局部变量一般是指在成员方法中定义的变量
3、java中作用域的分类
全局变量:也就是属性,作用域为整个类体
局部变量:也就是除了属性之外的其他变量均为局部变量,局部变量的作用域为定义它的代码块中。
4、全局变量可以不赋值,直接使用,因为有默认值。而局部变量必须赋值后,才能使用【局部变量没有默认值】。
public class OverLoad01{
public static void main(String[] args){
Test t = new Test();
t.cry();
t.eat();
}
}
class Test{
// 全局变量,也就是属性,作用域为整个类体 Test 类,在方法中也可以使用
// 全局变量可以不赋值,有默认值。
int age = 18;
public void cry(){
// 1、局部变量一般是指在成员方法中定义的变量。
// 局部变量必须赋值,进行初始化,局部变量是没有默认值的。
// 2、n和name就是 局部变量
// 3、n 和 name 的作用域在 cry 方法中使用。
int n = 10;
String name = "Jack";
System.out.println("在 cry 中使用属性 age = " + age);
System.out.println("在 cry 中使用局部变量 n = " + n);
}
public void eat(){
System.out.println("在eat中使用属性 age = " + age);
}
}
注意事项和使用细节【背下来】
1、属性和局部变量可以重名,访问时遵循就近原则;
2、在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名。
3、属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。而局部变量的生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。
4、作用域的范围不同
全局变量:可以在本类中或者在其他类(通过对象调用)中使用。
局部变量:只能在本类中对应的方法中使用(主要是受作用域影响)
public class OverLoad01{
public static void main(String[] args){
Test t = new Test();
Persion p = new Persion();
p.sayAge(t);
}
}
class Persion{
public void sayAge(Test t){
// 可以调用Test类中的全局变量,但是局部变量不可以打印出来。
System.out.println("年龄为" + t.age);
}
}
class Test{
int age = 18;
public void cry(){
int n = 10;
System.out.println("全局变量 age 为:" + age);
}
}
5、修饰符不同
全局变量/属性 可以加修饰符。【public、protected、default、private】
局部变量不可以加修饰符。
构造器
在创建对象的时候,将对象对应的属性 传入 进去,这就是构造器的作用。
基本语法
[修饰符] 方法名(形参列表){
方法体;
}
结构说明:
-
修饰符说明:构造器的修饰符可以为默认。 [修饰符] → []代表修饰符可有可无。
-
构造器没有返回值。
-
方法名定义说明: 构造器的 方法名 和 类名 必须一样。
-
形参列表说明: 和定义 成员方法 的规则一样。
-
构造器的调用,由系统来完成,即在new对象的时候,完成构造器。
基本介绍
构造方法又叫 构造器[constructor], 是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。
特点:
-
方法名和类名相同
-
没有返回值
-
在创建对象时,系统会自动的调用该类的构造器 完成该 对象的初始化。
public class Constructor{
// 编写一个main方法
public static void main(String[] args){
// 当我们new一个对象时,直接通过构造器指定名字和年龄,形式和调用成员方法时一致。
Person p1 = new Person("Jack", 80);
System.out.println("p1的名字为:" + p1.name);
}
}
// 创建Person类,在类中声明构造器
class Person{
String name;
int age;
// 声明构造器
// 1、构造器没有返回值,所以不能写void
// 2、构造器的名称和类名一致
// 3、(String pName, int pAge) 是构造器的形参列表,参数的定义规则和成员方法的形参规则一致。
public Person(String pName, int pAge){
System.out.println("构造器被调用");
name = pName;
age = pAge;
}
}
注意事项和使用细节
1、一个类中可以定义多个不同的构造器,即 构造器 重载。
public class Constructor{
// 编写一个main方法
public static void main(String[] args){
// 当我们new一个对象时,直接通过构造器指定名字和年龄,形式和调用成员方法时一致。
Person p1 = new Person("Jack", 80);
System.out.println("p1的年龄为:" + p1.age);
Person p2 = new Person("Jack");
System.out.println("p2的年龄为:" + p2.age);
}
}
// 创建Person类,在类中声明构造器
class Person{
String name;
int age;
// 声明第一个构造器
public Person(String pName, int pAge){
System.out.println("第一个构造器被调用");
name = pName;
age = pAge;
}
// 声明第二个构造器
public Person(String pName){
System.out.println("第二个构造器被调用");
name = pName;
}
}
2、构造器和类名要相同。
3、构造器没有返回值。
4、构造器是完成对象的初始化,当调用构造器时,对象已经在堆中创建了。
5、在创建对象时,系统会自动的调用该类的构造器。
6、如果没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)。
可通过javap来反编译 class文件【字节码文件】
public class Constructor{
// 编写一个main方法
public static void main(String[] args){
Person p = new Person();
}
}
// 创建Person类,在类中声明构造器
class Person{
// // 默认构造器格式
// Person(){
// }
}
7、一旦定义了自己的构造器,那么默认的构造器就被覆盖,如果还想用默认的无参构造器,则需要进行重载无参构造器。
对象创建的调用机制【关键】
this
什么是this
java虚拟机 会给每个对象分配 this, this代表当前对象。
内存图
jvm内存中对this的理解,可理解为 this 指向自身。
this的注意事项和使用细节
1、this关键字可以用来访问本类的方法、属性、构造器
2、this用于区分当前类的属性和局部变量。当局部变量和属性重名时,可通过this进行区分。
3、访问成员方法的语法为: this.方法名(参数列表);
4、访问构造器语法: this(参数列表);
注意此方法只能在构造器中使用【即只能在构造器中访问另一个构造器「重载」】, 且该语句必须放在构造器的第一个语句。
public class This01{
public static void main(String[] args){
T t = new T();
}
}
class T{
/*
细节:this访问构造器语法: this(参数列表);
注意只能在构造器中使用,且必须放在构造器的第一行
*/
public T(){
this("Jack", 10);
System.out.println("无参构造器");
}
public T(String name, int age){
System.out.println("T(String name, int age)构造器");
}
}
5、this不能在定义类的外部使用,只能在类定义的方法中使用。
本章作业
public class Music01{
public static void main(String[] args){
PassObject po = new PassObject();
Circle c = new Circle();
po.printAreas(c, 5);
}
}
class Circle{
double radius; // 半径
// 此处不写构造器,防止重载默认构造器
public double findArea(){
return Math.PI * this.radius * this.radius;
}
}
class PassObject{
public void printAreas(Circle c, int times){
if (times >= 0){
for (int i=1; i<=times; i++) {
c.radius = i;
System.out.println("半径为" + i + "面积为:" + c.findArea());
}
} else{
System.out.println("输入的times要大于等于0");
}
}
}
0261_韩顺平Java_本章作业11_哔哩哔哩_bilibili