Chaoter07 面向对象 (Object)
Chapter07 面向对象
7.1 类与对象
类是抽象的,概念的,代表一类事物,比如人类,猫类.., 即它是数据类型.
对象是具体的,实际的,代表一个具体事物, 即 是实例.
类是对象的模板,对象是类的一个个体,对应一个实例
public class Object01 {
public static void main(String[] args) {
//使用 OOP 面向对象解决
//实例化一只猫[创建一只猫对象]
//老韩解读
//1. new Cat() 创建一只猫(猫对象)
//2. Cat cat1 = new Cat(); 把创建的猫赋给 cat1
//3. cat1 就是一个对象
Cat cat1 = new Cat();
cat1.name = "小白";
cat1.age = 3;
cat1.color = "白色";
cat1.weight = 10;
//创建了第二只猫,并赋给 cat2
//cat2 也是一个对象(猫对象)
Cat cat2 = new Cat();
cat2.name = "小花";
cat2.age = 100;
cat2.color = "花色";
cat2.weight = 20;
//怎么访问对象的属性呢
System.out.println("第 1 只猫信息" + cat1.name
+ " " + cat1.age + " " + cat1.color + " " + cat1.weight);
System.out.println("第 2 只猫信息" + cat2.name
+ " " + cat2.age + " " + cat2.color + " " + cat2.weight);
}
}
//使用面向对象的方式来解决养猫问题
//定义一个猫类 Cat -> 自定义的数据类型
class Cat {
//属性/成员变量
String name; //名字
int age; //年龄
String color; //颜色
//double weight; //体重
//行为
}
7.1.1 对象在内存中的存在形式 (重要)
7.1.2 属性 / 成员变量
- 基本介绍:从概念或叫法上看: 成员变量 = 属性 = field(字段) (即 成员变量是用来表示属性的,授课中,统一叫 属性) 案例演示:Car(name,price,color)
- 细节
- 属性的定义语法同变量,示例:
访问修饰符 属性类型 属性名;
- 这里老师简单的介绍访问修饰符: 控制属性的访问范围 有四种访问修饰符 public, proctected, 默认, private ,后面我会详细介绍
- 属性的定义类型可以为任意类型,包含基本类型或引用类型
- 属性如果不赋值,有默认值,规则和数组一致。具体说:
int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000, boolean false,String null
public class PropertiesDetail {
public static void main(String[] args) {
//创建Person对象
//p1 是对象名(对象引用)
//new Person() 创建的对象空间(数据)才是真正的对象
Person p1 = new Person();
//对象的属性默认值,遵守数组规则
// 属性如果不赋值,有默认值,规则和数组一致。具体说: int 0,short 0, byte 0, long 0,
// float 0.0,double 0.0,char \u0000,
// boolean false,String null
System.out.println("输出这个人的信息");
System.out.println("age=" + p1.age + " name=" + p1.name
+ " sal=" + p1.sal + " isPass=" + p1.isPass);
}
}
class Person{
//四个属性
int age;
String name;
double sal;
boolean isPass;
}
7.1.3 类与对象的内存分配机制(重要)
- 栈: 一般存放基本数据类型(局部变量)
- 堆: 存放对象(Cat cat , 数组等)
- 方法区:常量池(常量,比如字符串), 类加载信息
- 示意图 [Cat (name, age, price)
- 赋值:赋的是地址值
Object03
public class Object03 {
public static void main(String[] args) {
Person1 p1 = new Person1();
p1.age = 10;
p1.name = "小明";
Person1 p2 = p1; //把p1 赋给了 p2 ,也就是把 p1 地址值赋给了 p2
System.out.println(p2.age);//10
}
}
class Person1{
//四个属性
int age;
String name;
}
Java创建对象的流程简单分析
Person p = new Person();
p.name = “jack”;
p.age = 10;
1) 先加载 Person 类信息(属性和方法信息, 只会加载一次)
2) 在堆中分配空间, 进行默认初始化(看规则)
3) 把地址赋给 p , p 就指向对象
4) 进行指定初始化, 比如 p.name =”jack” p.age = 10;
Object04
Person a=new Person();
a.age=10;
a.name="小明";
Person b;
b=a;
System.out.println(b.name);//小明
b.age=200;
b =null; //把 b 的地址值变成 null
System.out.println(a.age);//200
System.out.println(b.age);//异常
7.2 成员方法
7.2.1 成员方法的定义
访问修饰符 返回数据类型 方法名(形参列表..) {//方法体 语句; return 返回值; }
- 形参列表:表示成员方法输入 cal(int n) , getSum(int num1,
- 返回数据类型:表示成员方法输出, void 表示没有返回值
- 方法主体:表示为了实现某一功能代码块
- return 语句不是必须的。
- 老韩提示: 结合前面的题示意图, 来理解
public class Method01 {
public static void main(String[] args) {
Person03 p1 = new Person03();
p1.cal02(5);
//调用getSum方法,同时num1 = 10;num2 = 20;
//把 方法 getSum 返回的值,赋给 变量 returnRes
int returnRes = p1.getSum(10,20);
System.out.println("getSum方法返回的值=" + returnRes);
}
}
class Person03{
String name;
int age;
//方法(成员方法)
//添加speak 成员方法,输出“我是一个好人”
//
public void cal01(){
System.out.println("我是一个好人");
}
//添加cal02 成员方法 该方法可以接收一个数 n , 计算 1 + …… + n 的结果
//老韩解读
//1.(int n )形参列表, 表示当前有一个 形式参数 n ,可以直接用户输入
public void cal02(int n ){
int sum = 0;
for (int i = 1; i <= n; i++){
sum += i;
}
System.out.println("cal02 的值为=" + sum);
}
//添加getSum成员方法,可以计算两个数的和
//public 表示方法是公开的
//int :表示方法执行后,返回一个int 值
//getSum:方法名
//(int num1, int num2)形式参数,2个形参,可以接收用户传入的两个数
//return res:表示把 res 的值,返回
public int getSum(int num1, int num2){
int res = num1 + num2;
return res;
}
}
- 遍历数组
public class Method02 {
public static void main(String[] args) {
//请遍历一个数组
int[][] map = {{0,0,1},{1,1,1},{1,1,3}};
//使用方法完成输出,创建Mytools对象,tool
MyTools tool = new MyTools();
tool.printArr(map);
//遍历map数组
// 传统解决方式:解决思路1
// 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("");
// }
}
}
//把输出功能,写在一个类的方法中,然后调用该方法
class MyTools{
public void printArr(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("");
}
}
}
7.2.2 方法的调用机制
7.2.3 方法使用的细节 1
- 访问修饰符 (作用是控制 方法使用的范围) 如果不写默认访问
- [有四种: public, protected, 默认, private], 具体在后面说
- 返回数据类型
- 一个方法最多有一个返回值 [思考,如何返回多个结果 返回数组 ]
- 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
- 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值; 而且要求返回值类型必须和 return 的 值类型一致或兼容
- 如果方法是 void,则方法体中可以没有 return 语句,或者 只写 return ;
- 方法名 遵循驼峰命名法,最好见名知义,表达出该功能的意思即可, 比如 得到两个数的和 getSum, 开发中按照规范
- 形参列表
- 方法可以有0个参数,也可以有多个参数,中间用逗号隔开, 比如:
getSum(int n1,int n2)
- 参数类型可以为任意类型,包含基本类型或引用类型, 比如:
printArr(int[]map)
- 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型的参数!【getSum)
- 方法定义时的参数称为形式参数,简称形参;方法调用时的传入参数称为实际参数,简称实参
- 实参和形参的类型要一致或兼容、个数、顺序必须一致!
- 方法体
- 里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,但里面不能再定义方法!即:方法不能嵌套定义。
public class MethodDetail {
public static void main(String[] args) {
AA a = new AA();
int[] res = a.getSumAndSub(2,3); //2,3实参
System.out.println("差=" + res[0]);
System.out.println("和=" + res[1]);
}
}
class AA {
//1. 一个方法最多有一个返回值 [思考,如何返回多个结果 返回数组]
public int[] getSumAndSub(int n ,int m){ // n , m 形参
int[] resArr = new int[2];
resArr[0] = n - m;
resArr[1] = n + m;
return resArr;//可以通过返回数组来实现
// 2.返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)
// 具体看 getSumAndSub
//3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值;
// 而且要求返回值类型必须和 return
public double f1(){
double d1 = 1.1 * 3;
int n = 100;
// return n;
return d1; //类型兼容
//4.如果方法是 void,则方法体中可以没有 return 语句,或者 只写 return ;
public void f2(){
System.out.println("hello world");
// return ;//可以写,但不要代值
}
// 5.老韩提示:在实际工作中,我们的方法都是为了完成某个功能,所以方法名要有一定含义
// 最好是见名知意
}
}
}
7.2.4 方法调用的细节 2
- 同一个类中的方法调用:直接调用即可。
比如print(参数); 案例演示:A类sayOk 调用print()
- 跨类中的方法A类调用B类方法:需要通过对象名调用。
比如对象名.方法名(参数); 案例演示:B类sayHello调用print()
public class MethodDetail02 {
public static void main(String[] args) {
A a = new A();
a.sayOk();
a.m1();
}
}
class A {
//同一个类中的方法调用:直接调用即可
public void print(int n ){
System.out.println("print()方法被调用 n=" + n);
}
public void sayOk(){ //sayOk调用print
print(10);
System.out.println("继续执行sayOk");
}
//跨类中的方法A类调用B类方法:需要通过对象名调用
public void m1(){
System.out.println("m1方法被调用");
//创建一个B类的对象
B b = new B();
b.hi();
System.out.println("m1() 继续执行");
}
}
class B{
public void hi(){
System.out.println("B类中的 hi()被执行");
}
}
7.2.5 练习题 (成员方法)
import java.util.Scanner;
public class MethodExercise01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你判断的数字");
int num = sc.nextInt();
AA01 a = new AA01();
if (a.b(num)){
System.out.println("是一个偶数");
}else {
System.out.println("是一个奇数");
}
//使用print方法
a.print(4,4,'#');
}
}
class AA01 {
// 编写类 AA ,有一个方法:判断一个数是奇数 odd 还是偶数, 返回 boolean
//思路:
//1.方法的返回类型 boolean
//2.方法的名字 b
//3.方法的形参(int n)
//4.方法体,判断
public boolean b(int n){
// if (n % 2 == 0){
// return true;
// }else {
// return false;
// }
// return n % 2 == 0 ? true:false;
return n % 2 == 0;
}
// 2) 根据行、列、字符打印 对应行数和列数的字符,比如:行:4,列:4,字符#,则打印相应的效果
//思路:
//1.方法的返回类型 void
//2.方法的名字 print
//3.方法的形参(int row ,int col,char c)
//4.方法体,循环
public void print(int row ,int col,char c){
for (int i = 1; i <= row; i++){
for (int j = 1; j <= col; j++){
System.out.print(c + " ");
}
System.out.println("");
}
}
}
7.2.6 成员方法的传参机制 (非常重要)
1. 基本数据类型的传参机制
- 结论:基本数据类型,传递的是值(值拷贝),形参的任何改变不会影响实参
public class MethodParameter01 {
public static void main(String[] args) {
int a = 10;
int b = 20;
//创建对象AA,名字obj
AA02 obj = new AA02();
obj.swap(a,b);
System.out.println("main方法中的a=" + a + "b=" + b);//a=10 b=20,因为主栈中的a,b 没有发生改变,所以这里不变
}
}
class AA02 {
public void swap(int a, int b){
//交换之前
System.out.println("\na和b交换前的值\na=" + a + "\tb=" + b);//a=10 b=20
//完成 a 和 b 的交换
int temp = a;
a = b;
b = temp;
// swap这个栈发生的变化不会影响到其他栈,所以,main方法中的a,b没有发生改变
System.out.println("\na和b交换后的值\na=" + a + "\tb=" + b);//a=20 b=10,
}
}
2. 引用数据类型的传参机制
- 结论:引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参
public class MethodParameter02 {
public static void main(String[] args) {
BB b = new BB();
int[] arr = {1, 2, 3};
b.Test100(arr);
System.out.println("main方法中的arr");
for (int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
}
class BB{
public void Test100(int[] arr){
arr[0] = 200;
System.out.println("Test100方法中的arr");
for (int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
}
3. 克隆对象
public class MethodExercise02 {
public static void main(String[] args) {
Person02 p = new Person02();
p.name = "joker";
p.age = 20;
MyTools01 tools = new MyTools01();
Person02 p2 = tools.copyPerson(p);
//到此 p 和 p2 是 Person 对象,但是是两个独立的对象,属性相同
System.out.println("p的属性 age =" + p.age + " 名字=" + p.name);
System.out.println("p2的属性 age =" + p2.age + " 名字=" + p2.name);
//这里老师提示: 可以同 对象比较看看是否为同一个对象
System.out.println(p == p2);//false
}
}
class Person02{
String name;
int age;
}
class MyTools01{
// 编写一个方法 copyPerson,可以复制一个 Person 对象,返回复制的对象。克隆对象, 注意要求得到新对象和原来的
// 对象是两个独立的对象,只是他们的属性相同
//编写方法的思路
//1. 方法的返回类型 Person
//2. 方法的名字 copyPerson
//3. 方法的形参 (Person p)
//4. 方法体, 创建一个新对象,并复制属性,返回即可
public Person02 copyPerson(Person02 p){
//创建一个对象
Person02 p2 = new Person02();
p2.name = p.name;//把原来对象的名字赋给p2.name
p2.age = p.age;//把原来对象的名字赋给p2.age
return p2;
}
}
4. 方法递归调用
- 简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂问题,同时可以让代码变 得简洁
4.1 打印问题
public class Recursion01 { //打印问题
public static void main(String[] args) {
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);
}
}
4.2 阶乘问题
public class Recursion01 { //打印问题
public static void main(String[] args) {
int res = t1.factorial(5);
System.out.println("res=" + res);
}
}
class T{
public int factorial(int n){
if (n == 1){
return 1;
}else {
return factorial(n - 1)*n;
}
}
}
4.3 递归的重要规则
- 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
- 方法的局部变量是独立的,不会相互影响,比如n变量
- 如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引用类型的数据.
- 递归必须向退出递归的条件逼近,否则就是无限递归,出现(StackOverflowError,死龟了)
- 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。
4.4 汉诺塔
public class HanoiTower {
//编写一个 main 方法
public static void main(String[] args) {
Tower tower = new Tower();
tower.move(5, 'A', 'B', 'C');
}
}
class Tower {
//方法
//num 表示要移动的个数, a, b, c 分别表示 A 塔,B 塔, C 塔
public void move(int num , char a, char b ,char c) {
//如果只有一个盘 num = 1
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);
}
}
}
4.5 迷宫
public class MiGong {
//编写一个 main 方法
public static void main(String[] args) {
//思路
//1. 先创建迷宫,用二维数组表示 int[][] map = new int[8][7];
//2. 先规定 map 数组的元素值: 0 表示可以走 1 表示障碍物
int[][] map = new int[8][7];
//3. 将最上面的一行和最下面的一行,全部设置为 1
for(int i = 0; i < 7; i++) {
map[0][i] = 1;
map[7][i] = 1;
}
//4.将最右面的一列和最左面的一列,全部设置为 1
for(int i = 0; i < 8; i++){
map[i][0] = 1;
map[i][6] = 1;
}
map[3][1] = 1;
map[3][2] = 1;
map[2][2] = 1; //测试回溯
// map[2][1] = 1;
// map[2][2] = 1;
// map[1][2] = 1;
//输出当前的地图
System.out.println("=====当前地图情况======");
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();
}
//使用 findWay 给老鼠找路
T0 t1 = new T0();
//下右上左
t1.findWay(map, 1, 1);
System.out.println("\n====找路的情况如下=====");
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();
}
}
}
class T0 {
//使用递归回溯的思想来解决老鼠出迷宫
//老韩解读
//1. findWay 方法就是专门来找出迷宫的路径
//2. 如果找到,就返回 true ,否则返回 false
//3. map 就是二维数组,即表示迷宫
//4. i,j 就是老鼠的位置,初始化的位置为(1,1)
//5. 因为我们是递归的找路,所以我先规定 map 数组的各个值的含义
// 0 表示可以走 1 表示障碍物 2 表示可以走 3 表示走过,但是走不通是死路
//6. 当 map[6][5] =2 就说明找到通路,就可以结束,否则就继续找.
// 7. 先确定老鼠找路策略 下->右->上->左
public boolean findWay(int[][] map , int i, int j) {
if(map[6][5] == 2) {//说明已经找到
return true;
} else {
if(map[i][j] == 0) {//当前这个位置 0,说明表示可以走
//我们假定可以走通
map[i][j] = 2;
//使用找路策略,来确定该位置是否真的可以走通
//下->右->上->左
if(findWay(map, i + 1, j)) {//先走下
return true;
} else if(findWay(map, i, j + 1)){//右
return true;
} else if(findWay(map, i-1, j)) {//上
return true;
} else if(findWay(map, i, j-1)){//左
return true;
} else {
map[i][j] = 3;
return false;
}
} else { //map[i][j] = 1 , 2, 3
return false;
}
}
}
//修改找路策略,看看路径是否有变化
//下->右->上->左 ==> 上->右->下->左
public boolean findWay2(int[][] map , int i, int j) {
if(map[6][5] == 2) {//说明已经找到
return true;
} else {
if(map[i][j] == 0) {//当前这个位置 0,说明表示可以走
//我们假定可以走通
map[i][j] = 2;
//使用找路策略,来确定该位置是否真的可以走通
//上->右->下->左
if(findWay2(map, i - 1, j)) {//先走上
return true;
} else if(findWay2(map, i, j + 1)){//右
return true;
} else if(findWay2(map, i+1, j)) {//下
return true;
} else if(findWay2(map, i, j-1)){//左
return true;
} else {
map[i][j] = 3;
return false;
}
} else { //map[i][j] = 1 , 2, 3
return false;
}
}
}
}
7.3 方法的重载
- java 中允许同一个类中,多个同名方法的存在,但要求形参列表不一致
- 方法名:必须相同
- 形参列表:必须不同(形参类型或个数或顺序,至少有一样不同参数名无要求)
- 返回类型:无要求
练习
public class OverLoadExercise {
//编写一个 main 方法
public static void main(String[] args) {
//在主类的 main ()方法中分别用参数区别调用三个方法
Methods method = new Methods();
method.m(10);//100
method.m(10, 20);//200
method.m("韩顺平教育 hello");//字符串信息
//测试
System.out.println(method.max(10, 24)); // 24
System.out.println(method.max(10.0, 21.4)); // 21.4
System.out.println(method.max(10.0, 1.4, 30.0)); // 30.0
}
}
/*
编写程序,类 Methods 中定义三个重载方法并调用。方法名为 m。
三个方法分别接收一个 int 参数、两个 int 参数、一个字符串参数。分别执行平方运算并输出结果,
相乘并输出结果,输出字符串信息。在主类的 main ()方法中分别用参数区别调用三个方法
定义三个重载方法 max(),第一个方法,返回两个 int 值中的最大值,
第二个方法,返回两个 double 值中的最大值,第三个方法,
返回三个 double 值中的最大值,并分别调用三个方法
*/
class Methods {
//分析
//1 方法名 max
//2 形参 (int,int)
//3.int
public int max(int n1, int n2) {
return n1 > n2 ? n1 : n2;
}
//分析
//1 方法名 max
//2 形参 (double,double)
//3.double
public double max(double n1, double n2) {
return n1 > n2 ? n1 : n2;
}
//分析
//1 方法名 max
//2 形参 (double,double,double)
//3.double
public double max(double n1, double n2, double n3) {
System.out.println("max(double n1, double n2, double n3)");
//求出 n1 和 n2 的最大值
double max1 = n1 > n2 ? n1 : n2;
return max1 > n3 ? max1 : n3;
}
public double max(double n1, double n2, int n3) {
System.out.println("max(double n1, double n2, int n3)");
//求出 n1 和 n2 的最大值
double max1 = n1 > n2 ? n1 : n2;
return max1 > n3 ? max1 : n3;
}
//分析
//1 方法名 m
//2 形参 (int)
//3.void
public void m(int n) {
System.out.println("平方=" + (n * n));
}
//1 方法名 m
//2 形参 (int, int)
//3.void
public void m(int n1, int n2) {
System.out.println("相乘=" + (n1 * n2));
}
//1 方法名 m
//2 形参 (String)
//3.void
public void m(String str) {
System.out.println("传入的 str=" + str);
}
}
7.4 可变参数
1.基本
概念:java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。 就可以通过可变参数实现
语法:访问修饰符 返回类型 方法名(数据类型... 形参名) { }
- 可变参数的实参可以为0个或任意多个。
- 可变参数的实参可以为数组。
- 可变参数的本质就是数组.
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
- 一个形参列表中只能出现一个可变参数
public class VarParameterDetail {
//编写一个 main 方法
public static void main(String[] args) {
//细节: 可变参数的实参可以为数组
int[] arr = {1, 2, 3};
TA t1 = new TA();
t1.f1(arr);
}
}
class TA {
public void f1(int... nums) {
System.out.println("长度=" + nums.length);
}
//细节: 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
public void f2(String str, double... nums) {
}
//细节: 一个形参列表中只能出现一个可变参数
//下面的写法是错的.
// public void f3(int... nums1, double... nums2) {
// }
}
练习:
public class VarParameterExercise {
//编写一个 main 方法
public static void main(String[] args) {
HspMethod hm = new HspMethod();
System.out.println(hm.showScore("milan" , 90.1, 80.0 ));
System.out.println(hm.showScore("terry" , 90.1, 80.0,10,30.5,70 ));
}
}
class HspMethod {
/*
有三个方法,分别实现返回姓名和两门课成绩(总分),
返回姓名和三门课成绩(总分),返回姓名和五门课成绩(总分)。
封装成一个可变参数的方法
*/
//分析 1. 方法名 showScore 2. 形参(String ,double... ) 3. 返回 String
//听课小伙伴,老师要求必须自己动手写
public String showScore(String name,double...score){
double totalScore = 0;
for(int i = 0; i < scores.length; i++) {
totalScore += scores[i];
}
return name + " 有 " +scores.length + "门课的成绩总分为=" + totalScore;
}
}
7.5 作用域(Scope)(重要)
1. 基本使用
- 在java编程中,主要的变量就是属性(成员变量)和局部变量。
- 我们说的局部变量一般是指在成员方法中定义的变量。【举例Cat类: cry)java中作用域的分类
- 全局变量:也就是属性,作用域为整个类体Cat类:cry eat等方法使用属性【举例】
- 局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中!
- 全局变量(属性)可以不赋值,直接使用,因为有默认值,
局部变量必须赋值后,才能使用,因为没有默认值
。[举例】
public class VarScope {
//编写一个 main 方法
public static void main(String[] args) {
}
}
class Cat {
//全局变量:也就是属性,作用域为整个类体 Cat 类:cry eat 等方法使用属性
//属性在定义时,可以直接赋值
int age = 10; //指定的值是 10
//全局变量(属性)可以不赋值,直接使用,因为有默认值,
double weight; //默认值是 0.0
public void hi() {
//局部变量必须赋值后,才能使用,因为没有默认值
int num = 1;
String address = "北京的猫";
System.out.println("num=" + num);
System.out.println("address=" + address);
System.out.println("weight=" + weight);//属性
}
public void cry() {
//1. 局部变量一般是指在成员方法中定义的变量
//2. n 和 name 就是局部变量
//3. n 和 name 的作用域在 cry 方法中
int n = 10;
String name = "jack";
System.out.println("在 cry 中使用属性 age=" + age);
}
public void eat() {
System.out.println("在 eat 中使用属性 age=" + age);
//System.out.println("在 eat 中使用 cry 的变量 name=" + name);//错误
}
}
2. 细节
- 属性和局部变量可以重名,访问时遵循就近原则。
- 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。[举例]
- 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。
- 局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁.即在一次方法调用过程中。
- 作用域范围不同
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用) - 局部变量:只能在本类中对应的方法中使用
修饰符不同
全局变量/属性可以加修饰符局部变量不可以加修饰符
public class VarScopeDetail{
//编写一个 main 方法
public static void main(String[] args) {
Person p1 = new Person();
/*
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。
局部变量,生命周期较短,伴随着它的代码块的执行而创建,
伴随着代码块的结束而销毁。即在一次方法调用过程中
*/
//p1.say();//当执行 say 方法时,say 方法的局部变量比如 name,会创建,当 say 执行完毕后
//name 局部变量就销毁,但是属性(全局变量)仍然可以使用
//
TAC t1 = new TAC();
t1.test(); //第 1 种跨类访问对象属性的方式
t1.test2(p1);//第 2 种跨类访问对象属性的方式
}
}
class TAC {
//全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
public void test() {
Person p1 = new Person();
System.out.println(p1.name);//jack
}
public void test2(Person p) {
System.out.println(p.name);//jack
}
}
class Person {
//细节: 属性可以加修饰符(public protected private..)
// 局部变量不能加修饰符
public int age = 20;
String name = "jack";
public void say() {
//细节 属性和局部变量可以重名,访问时遵循就近原则
String name = "king";
System.out.println("say() name=" + name);
}
public void hi() {
String address = "北京";
//String address = "上海";//错误,重复定义变量
String name = "hsp";//可以
}
}
7.6 构造器
7.6.1 基本语法
[修饰符] 方法名(形参列表){
方法体;
}
1)构造器的修饰符可以默认, 也可以是 public protected private
2) 构造器没有返回值
3) 方法名 和类名字必须一样
4) 参数列表 和 成员方法一样的规则
5) 构造器的调用, 由系统完成
7.6.3 基本介绍
构造方法又叫
构造器(constructor)
,是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:
- 方法名和类名相同
- 没有返回值
- 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。
7.6.4 快速入门
public class Constructor01 {
//编写一个 main 方法
public static void main(String[] args) {
//当我们 new 一个对象时,直接通过构造器指定名字和年龄
Person002 p1 = new Person002("smith", 80);
System.out.println("p1 的信息如下");
System.out.println("p1 对象 name=" + p1.name);//smith
System.out.println("p1 对象 age=" + p1.age);//80
}
}
//在创建人类的对象时,就直接指定这个对象的年龄和姓名
//
class Person002 {
String name;
int age;
//构造器
//老韩解读
//1. 构造器没有返回值, 也不能写 void
//2. 构造器的名称和类 Person 一样
//3. (String pName, int pAge) 是构造器形参列表,规则和成员方法一样
public Person002(String pName, int pAge) {
System.out.println("构造器被调用~~ 完成对象的属性初始化");
name = pName;
age = pAge;
}
}
7.6.5 构造器的细节
- 一个类可以定义多个不同的构造器,即构造器重载
- 比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄
- 构造器名和类名要相同
- 构造器没有返回值
- 构造器是完成对象的初始化,并不是创建对象在创建对象时,系统自动的调用该类的构造方法
- 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如
Dog (){ }
,使用javap
指令反编译看看- 一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下,即:
Dog(){ }
写(这点很重要
public class ConstructorDetail {
public static void main(String[] args) {
Person006 person006 = new Person006("King", 40);
Person006 person0061 = new Person006("tom");
Dog dog1 = new Dog();//使用的是默认的无参构造器
}
}
class Person006{
String name;
int age;//默认值 = 0
//第一个构造器
public Person006(String pName, int pAge){
name = pName;
age = pAge;
}
//第二个构造器,只指定人名
public Person006(String pName){
name = pName;
}
}
class Dog {
//如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)
//使用 javap 指令 反编译看看
/*
默认构造器
Dog() {
}
*/
//一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,
//除非显式的定义一下,即: Dog(){} 写 (这点很重要)
//
public Dog(String dName) {
//... }
Dog() { //显式的定义一下 无参构造器
}
}
7.7 This关键字
7.7.1 This关键字细节
-
this 关键字可以用来访问本类的属性、方法、构造器
-
this 用于区分当前类的属性和局部变量
-
访问成员方法的语法:this.方法名(参数列表);
-
访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一 条语句)
-
this 不能在类定义的外部使用,只能在类定义的方法中使用。
public class ThisDetail {
public static void main(String[] args) {
T2 t2 = new T2();
}
}
class T2{
String name = "jack";
int num = 100;
// 细节4:访问构造器语法:this(参数列表); 注意只能在构造器中使用
// (即只能在构造器中访问另外一个构造器, 必须放在第一 条语句)
public T2(){
//这里去访问 T2(String name, int age).
//注意:即只能在构造器中访问另外一个构造器, 必须放在第一 条语句
this("jack",20);
System.out.println("T() 构造器");
}
public T2(String name, int age){
System.out.println("T2(String name, int age) 构造器");
}
// 细节3:访问成员方法的语法:this.方法名(参数列表);
public void f1(){
System.out.println("f1() 方法。");
}
public void f2(){
System.out.println("f2() 方法。");
//调用本类的f1
//第一种方式
f1();
//第二种方式
this.f1();
}
// 细节1:this 关键字可以用来访问本类的属性、方法、构造器
public void f3(){
System.out.println("name=" + this.name + "num=" + this.num);
}
}
7.7.2 练习
public class TestPerson {
//编写一个 main 方法
public static void main(String[] args) {
Person p1 = new Person("mary", 20);
Person p2 = new Person("mary", 20);
System.out.println("p1 和 p2 比较的结果=" + p1.compareTo(p2));
}
}
/*
定义 Person 类,里面有 name、age 属性,并提供 compareTo 比较方法,
用于判断是否和另一个人相等,提供测试类 TestPerson 用于测试, 名字和年龄完全一样,就返回 true, 否则返回 false
*/
class Person {
String name;
int age;
//构造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//compareTo 比较方法
public boolean compareTo(Person p) {
//名字和年龄完全一样
// if(this.name.equals(p.name) && this.age == p.age) {
// return true;
// } else {
// return false;
// }
return this.name.equals(p.name) && this.age == p.age;
}
}
总结:
- 简单说就是,哪个对象调用,this就代表哪个对象
本章作业
public class Homework01 {
public static void main(String[] args) {
A01 a01 = new A01();
double[] arr = {1.0,5.6,5.7};
Double res = a01.max(arr);
if (res != null) {
System.out.println(a01.max(arr));
} else {
System.out.println("arr的输入有误");
}
}
}
/*
编写类A01,定义方法max,实现求某个double数组的最大值,并返回
思路分析
1.类名 A01
2.方法名 max
3.形参 (double[])
4.返回值 double
*/
class A01{
public Double max(double[] num){
//先判断数组是否为空,再判断
if ( num != null && num.length > 0) {
double max = num[0];//假设第一个为最大值
for (int i = 0; i < num.length; i++) {
if (max < num[i]) {
max = num[i];
}
}
return max;
}else{
return null;
}
}
}
public class Homework02 {
public static void main(String[] args) {
String[] strs = {"jack", "tom","mary","milan"};
A02 a02 = new A02();
int index = a02.find("tom",strs);
if (index == -1) {
System.out.println("没有找到");
} else {
System.out.println(index);
}
}
}
//2.编写类A02,定义方法find,实现查找某字符串是否在字符串数组中,并返回索引,如果找不到,返回-1. Homework02.java
class A02{
public int find(String findStr, String[] strs) {
//直接遍历字符串数组,找到,返回索引值
for (int i = 0; i < strs.length; i++) {
if (findStr.equals(strs[i])) {
return i;
}
}
return -1;
}
}
public class Homework03 {
public static void main(String[] args) {
Book book = new Book("笑傲江湖",30);
book.info();
book.updatePrice();
book.info();
}
}
//3.编写类Book,定义方法updatePrice,实现更改某本书的价格,具体:
//如果价格>150,则更改为150,如果价格>100,更改为100,否则不变HomeworkO3.java-定要自己写代码
//分析
/*
1.类名 Book
2.属性 price name
3.方法名 updatePrice
4.形参()
5。返回值,void
*/
class Book{
String name;
int price;
public Book(String name, int price){
this.name = name;
this.price = price;
}
public void updatePrice(){
//如果方法中,没有 price 局部变量,this.price 等价 price
if (this.price > 150){
this.price = 150;
}else if (this.price > 100){
this.price = 100;
}
}
//显示书籍情况
public void info(){
System.out.println("书名=" + this.name + " " + "价格=" + this.price);
}
}
public class Homework04 {
public static void main(String[] args) {
int[] oldArr = {2,5,6,7};
A03 a03 = new A03();
int[] res = a03.copeArr(oldArr);
for (int i = 0; i < res.length; i++){
System.out.println(res[i]);
}
}
}
//4.编写类AO3,实现数组的复制功能copyArr,输入旧数组,返回一个新数组,元素和旧数组一样Homework04.java
class A03{
public int[] copeArr(int[] oldArr){
System.out.println("调用此方法");
int[] newArr = new int[oldArr.length];
for (int i = 0; i < oldArr.length; i++){
newArr[i] = oldArr[i];
}
return newArr;
}
}
public class Homework05 {
public static void main(String[] args) {
Circle circle = new Circle(5);
System.out.println("周长为:" + circle.c());
System.out.println("面积为:" + circle.s());
}
}
//5.定义一个圆类Circle,定义属性:半径,提供显示圆周长功能的方法,提供显示圆面积的方法HomeworkO5.java
class Circle{
double r;
public Circle(double r){
this.r = r;
}
public double c(){
double num = 2 * Math.PI * r;
return num;
}
public double s(){
double num = r * r * Math.PI;
return num;
}
}
public class Homework06 {
public static void main(String[] args) {
Cale cale = new Cale(5, 0);
System.out.println("两数相加=" + cale.sum());
System.out.println("两数相减=" + cale.minus());
System.out.println("两数相乘=" + cale.multiply());
Integer divRes = cale.div();
if (divRes != null) {
System.out.println("两数相除=" + cale.div());
}
}
}
//6.编程创建一个Cale计算类,在其中定义2个变量表示两个操作数,
//定义四个方法实现求和、差、乘、商(要求除数为0的话,要提示)并创建两个对象,分别测试
class Cale{
int a;
int b;
public Cale(int a,int b){
this.a = a;
this.b = b;
}
public int sum(){
return a + b;
}
public int minus(){
return a - b;
}
public int multiply(){
return a * b;
}
public Integer div() {
//判断
if (b == 0) {
System.out.println("除数不能为0");
return null;
} else {
return a / b;
}
}
}
public class Homework07 {
public static void main(String[] args) {
Dog01 dog01 = new Dog01("小花", 2, "花色");
dog01.show();
}
}
class Dog01{
String name;
int age;
String color;
public Dog01(String name, int age, String color){
this.name = name;
this.age = age;
this.color = color;
}
public void show(){
System.out.println("狗名是=" + name + "年龄是=" + age + "颜色是=" + color );
}
}
public class Homework09 {
public static void main(String[] args) {
Music music = new Music("哈哈哈", 36);
music.play();
System.out.println(music.getInfo());
}
}
//9.定义Music类,里面有音乐名name、音乐时长times属性,
// 并有播放play功能和返回本身属性信息的功能方法getInfo. Homework09.java
class Music{
String name;
double times;
public Music(String name,double times){
this.name = name;
this.times = times;
}
public void play(){
System.out.println("音乐" + name + "正在播放中……时长" + times + "秒");
}
public String getInfo(){
return "音乐" + name + "播放时间为" + times;
}
}
class Demo {
int i = 100;
public void m() {
int j = i++;
System.out.println("i=" + i);//101
System.out.println("j=" + j);//100
}
}
class Homework10 {
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = d1;//把d1 值赋给d2
d2.m();
//下面两句话指向的都是同一个对象
System.out.println(d1.i);//101
System.out.println(d2.i);//101
}
}
public class Homework12 {
public static void main(String[] args) {
}
}
//12.创建一个Employee类,属性有(名字,性别,年龄,职位,薪水),提供3个构造方法,
//可以初始化(1)(名字,性别,年龄,职位,薪水),(2)(名字,性别,年龄)(3)(职位,薪水).要求充分复用构造器Homework12.java
class Employee{
String name;
char gender;
int age;
String posts;//职位
double salary;
public Employee(String name, char gender,int age){
this.name = name;
this.gender = gender;
this.age = age;
}
public Employee(String posts,double salary){
this.posts = posts;
this.salary = salary;
}
public Employee(String name, char gender,int age,String posts,double salary){
this(name,gender,age);//这就是复用构造器,用this调用别的构造器,必须排在第一位,即后面不能在调用其他的构造器
this.posts = posts;
this.salary = salary;
}
}
猜拳
public class Homework13 {
public static void main(String[] args) {
Circle02 circle = new Circle02();
PassObject po = new PassObject();
po.printAreas(circle,5);
}
}
/*
13.将对象作为参数传递给方法。Homework13.java题目要求:
(1)定义一个Circle类,包含一个double型的radius属性代表圆的半径,findArea()方法返回圆的面积。
(2)定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:
public void printAreas(Circle c, int times)//方法签名/声明
(3)在printAreas方法中打印输出1到times之间的每个整数半径值,以及对应的面积。例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。
(4)在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如图所示,
老韩提示,如果还是不明确,自己画一个内存图,一定可以搞定
*/
class Circle02{ //类
double radius; //半径
public Circle02(){ //无参构造器
}
public Circle02(double radius){
this.radius = radius;
}
public double findArea(){
return radius * radius * Math.PI;
}
//添加一个方法setRadius,修改对象的半径值
public void setRadius(double radius){
this.radius = radius;
}
}
class PassObject{
public void printAreas(Circle02 c,int times){
System.out.println("radius\tarea");
for ( int i = 1; i <= times; i++){
c.setRadius(i);
System.out.println((double) i + "\t" + c.findArea());
}
}
}
import java.util.Random;
import java.util.Scanner;
public class Homework14 {
public static void main(String[] args) {
//创建一个玩家对象
Tom t = new Tom();
//用来记录最后输赢的次数
int isWinCount = 0;
//创建二维数组,用来接收局数,Tom出拳情况及电脑出拳情况
int[][] arr1 = new int[3][3];
int j = 0;
//创建一个一维数组,用来接受输赢情况
String[] arr2 = new String[3];
//获取玩家的出拳
Scanner sc = new Scanner(System.in);
for (int i = 0; i < 3; i++) {
System.out.println("数字0-表示石头,数字1-表示剪刀,数字2-表示布");
System.out.println("请输入你的数字");
int tomNum = sc.nextInt();
t.setTomGuessNum(tomNum);
int tomGuess = t.getTomGuessNum();
arr1[i][j + 1] = tomGuess;
//获取电脑的出拳
int comGuess = t.computerNum();
arr1[i][j + 2] = comGuess;
//将玩家猜的拳与电脑做对比
String isWin = t.vsComputer();
arr2[i] = isWin;
arr1[i][j] = t.count;
//对每一局的情况进行输出
System.out.println("===========================");
System.out.println("局数\t玩家的出拳\t电脑的出拳\t输赢情况");
System.out.println(t.count + "\t" + tomGuess + "\t\t" + comGuess + "\t\t" + isWin);
System.out.println("===========================");
System.out.println("\n\n");
isWinCount = t.winCount(isWin);
}
//对游戏的最终结果进行输出
System.out.println("局数\t玩家出拳\t电脑出拳\t\t输赢情况");
for (int a = 0; a < arr1.length; a++){
for (int b = 0; b < arr1[a].length; b++){
System.out.println(arr1[a][b] + "\t\t\t");
}
System.out.println(arr2[a]);
System.out.println();
}
System.out.print("你赢了" + isWinCount + "次");
}
}
/*
思路
1.随机生成0,1,2
*/
class Tom{
//玩家出拳
int tomGuessNum;
//电脑出拳
int comGuessNum;
//赢的次数
int winCountNum;
//比赛的次数
int count = 1;//一共比赛三次
//电脑随机猜拳的数字的方法
public int computerNum(){
Random r = new Random();
comGuessNum = r.nextInt(3);// 方法 返回 0 - 2的随机数
return comGuessNum;
}
//规定玩家的玩法
public void setTomGuessNum(int tomGuessNum){
if (tomGuessNum > 2 || tomGuessNum < 0){
throw new IllegalArgumentException("数字输入有误");
}
this.tomGuessNum = tomGuessNum;
}
public int getTomGuessNum(){
return tomGuessNum;
}
//比较
public String vsComputer(){
if (tomGuessNum == 0 && comGuessNum == 1){
return "你赢了";
}else if (tomGuessNum == 1 && comGuessNum ==2) {
return "你赢了";
}else if (tomGuessNum == 2 && comGuessNum ==0) {
return "你赢了";
}else if (tomGuessNum == comGuessNum){
return "平手";
}else {
return "你输了";
}
}
public int winCount(String s){
count++;
if (s.equals("你赢了")){
winCountNum++;
}
return winCountNum;
}
}