面向对象编程(基础)
- 本人是初学者,跪求各位路过的大佬给点建议,真心想学习进步,先在这谢过各位姥爷了
1. 类与对象
-
引入案例
/* 张老太养了两只猫猫;一只名字叫小白,今年3岁,白色。还有一只叫叫小花,今年100岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的民字,年龄,颜色。如果用户输入的小猫名错误,则显示张老太没有这只猫猫。 */ // 传统方法解决 public class object01{ public static void main(String arr[]){ String cat1Name = "小白"; String cat2Name = "小花"; int cat1Age = 3; int cat2Age = 100; String cat1Color = "Write"; String cat2Coloe = "many color"; } } // 可以做出来,但是始终无法体现猫的行为,效率较低
-
引入类与对象
public class object02{ public static void main(String arg[]){ // 1.new Cat() 创建一只猫 // 2.Cat cat1 = new Cat(); 把创建的猫赋给 cat1 Cat cat1 = new Cat(); cat1.name = "小白"; cat1.age = 3; cat1.color = "白色"; //创建第二只猫 Cat cat2 = new Cat(); cat2.name = "小花"; cat2.age = 100; cat2.color = "花色"; //访问对象的属性 System.out.println(cat1.name + cat1.age + cat1.color); } } class Cat{ //属性 String name; int age; String color; } //字符串存放在常量池内,int型数据直接放在堆内
-
-
概述
- 一个程序就是一个世界
-
属性/成员变量/字段(field)
- 属性的定义语法类变量 访问修饰符 属性类型 属性名;
- 访问修饰符:控制属性的访问范围
- 有四种访问修饰符 public,proctected,默认,private
- 注意:属性如果不赋值,有默认值,规则和数组一致
-
创建对象
-
先声明在创建
Cat cat; cat = new Cat();
-
直接创建
Cat cat = new Cat();
-
-
访问属性
-
基本语法:对象名.属性名;
-
思考题
-
Person p1 = new Person(); p1.age = 10; p1.name = "小明"; Person p2 = p1; //把 p1 赋给了 p2,也可以说是让 p2 指向 p1(内存空间由此可知) //即 p2 和 p1 一样都是指向一个内存地址 //而不是新开辟内存地址再将 p1 的内容装填进去 System.out.println(p2.age);
-
把 p1 赋给了 p2,也可以说是让 p2 指向 p1(内存空间由此可知)
-
即 p2 和 p1 一样都是指向一个内存地址
-
而不是新开辟内存地址再将 p1 的内容装填进去
-
-
Java内存的结构分析:
- 栈:一般存放基本数据类型(局部变量)
- 堆:存放对象(Cat cat,数组等)
- 方法区:常量池(常量,比如字符串),类加载信息
- Person类信息只加载一次,多次加载无意义
2.成员方法
-
引入案例
-
成员不仅有属性,还有方法(行为)
-
/* 1.添加一个speak成员方法,输出 我是一个好人 2.添加cal01成员方法,可以计算从1 + ... + 1000的结果 3.添加cal02成员方法,该方法可以接受一个数n,计算从1 + .. + n 4.添加一个getSum成员方法,可以计算两个数的和 */
-
public class Method1{ public static void main(String arg[]){ // 创建对象 Person p1 = new Person(); p1.speak(); //调用方法,只有调用才会执行 } } class Person{ String name; //常量池 int age; //堆中 //添加方法 1 public void speak(){ System.out.println("我是一个好人"); } //添加方法 2 public void cal01(){ int res = 0; for(int i = 0; i < 1001; i++){ res = res + i; } System.out.println(res); } //添加方法 3 动态版2 public void cal02(int n){ //调用方法时给方法需要传递参数 int res = 0; for(int i = 0; i < n+1; i++){ res += i; } System.out.println(res); } //添加方法 4 类似方法3 }
-
-
内存机制
- 方法调用内存机制:
- 当程序执行到方法时,就会开辟一个独立的空间(栈空间)
- 当方法执行完毕,或者执行到return语句时,就会返回
- 返回到调用方法的地方
- 返回后,继续执行方法后面的代码
- 当main方法(栈),执行完毕,整个程序退出
- 方法调用内存机制:
-
方法调用说明
- 同一个类中的方法调用:直接调用即可。比如print(参数);
- 跨类中的方法A类调用B类方法:需要通过对象名调用。比如 对象名.方法名(参数);
- 特别说明:跨类的方法调用和方法的访问修饰符相关。
-
方法的参数传递机制
-
public class Method{ public static void main(String arr[]){ Person p = new Person(); p.name = "jack"; p.age = 10; b.test200(p); System.out.prinln("main 的 p.age = "+ p.age); //此时输出10 } } class Person{ String name; int age; } class B{ public void test200(Person){ p = null; // 这里改变的是传递过来的形参(也可以理解为切断了形参的指针) // 也就是说原来的p没有任何改变(也可以理解为原来的指针没有任何改变) } }
-
案例强化:
/* 编写一个方法copyPerson,可以复制一个Person类对象,返回复制的对象。克隆对象。 注意:要求得到新对象和原来的对象是两个独立的对象,只是他们的属性像同。 */
public class MethodExercise02{ public static void main(String arr[]){ Person p = new Person(); p.name = "milan"; p.age = 100; Mytools tool = new Mytools(); tool.copyPerson(p); //并且此时p和p2是两个独立的对象,并且属性相同 } } class Person{ String name; int age; } class Mytools{ public Person copyPerson(person p){ Person p2 = new Person(); //new出来的p2,所以p2指向新的空间(独立) p2.name = p.name; p2.age = p.age; } }
-
-
方法递归调用
-
打印问题
public class Recursion01{ public static void main(String arr[]){ T t1 = new T(); t1.test(4); } } class T{ public void test(int n){ if(n > 2){ test(n-1); //递归打印 } Ststem.out.println("n="+n); } }
-
阶乘
public class Recursion02(int n){ T t1 = new T(); t1.factorial(5); //输出120 } class T{ public int factorial(int n){ if(n == 1) return 1; else return factorial(n-1) * n; } }
-
斐波那契数列
public class Fibonacci{ public static void main(String arr[]){ } } class Fib{ public int Fibo(int n){ if(0 < n && n < 3){ return 1; } else if(n > 2) return Fibo(n-1) + Fibo(n-2); else Ssytem.out.println("输入格式错误!"); }
-
猴子吃桃子
/* 有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个;以后猴子每天都吃其中的一半,然后还多吃一个。当到第10天的时候,想再吃时(此时还没吃),发现桃子只有一个了。问题:最初有多少个桃子? */
-
迷宫问题
- 二维数组表示迷宫地图;
- 元素值为1时表示障碍物,元素值为0时表示有路可以走
- 要求:测试回溯
public class MiGong{ public static void main(String arr[]){ //1.创建迷宫地图 int[][] map = new int[8][7]; for(int i = 0; i < 7; i++){ map[0][i] = 1; map[7][i] = 1; } for(int i = 0; i < 7; i++){ map[i][0] = 1; map[i][6] = 1; } map[3][1] = 1; map[3][2] = 1; T t1 = new T(); t1.findWay(map,1,1);//将当前位置传给findWay()方法 } } class T{ public boolean findWay(int[][] map, int i, int j){ if(map[6][5]==2){ //2表示已经走过,并且可以走通 return true; //说明已经找到了 }else{ if(map[i][j] = 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; //表示该路走不通,标记为3 return false; } } } //改变策略 public boolean findWay2(int[][] map, int i, int j){ if(map[6][5]==2){ //2表示已经走过,并且可以走通 return true; //说明已经找到了 }else{ if(map[i][j] = 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; //表示该路走不通,标记为3 return false; } } } } }
-
汉诺塔
-
三根柱子,A、B、C,其中A主子上有64个圆盘,按从大到小从底摆放,要将这64根柱子移动到C柱上,且移动过程中不能将较小的盘子放到较大的盘子上将64个盘子简化成5个盘子解决问题
-
需求:将移动盘子的过程打印出来、将移动的次数记录并打印出来
-
思考:最少移动多少次即可完成
-
八皇后
-
在8 * 8的国际象棋棋盘上摆放八个皇后,使其任意两个皇后都不能同处与同一行,同一列或同一斜线上,问有多少种摆法。
-
-
方法重载
- 概述:Java中允许在同一个类中,多个同名方法的存在,但要求形参列表不一致(顺序或者类型),返回类型不同不构成重载。
3.可变参数
-
概述:Java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
-
基本语法:访问修饰符 返回类型 方法名(数据类型…形参名) {}
-
快速入门:
public class VarParameter01{ public static void main(String arr[]){ } } class HspMethod{ //可以计算 2 个数的和,3个数的和,等等... //可以使用方法重载(麻烦到死) //可变参数 public int sum(int... nums){ // int... 表示接受的是可变参数,类型是int,即可以接收多个int // 使用时可以当作数组使用 System.out.println("接收的参数个数是"+nums.length); } }
- 注意:
- 可变参数的实参可以是0个或任意多个;
- 可变参数的实参可以为数组;
- 可变参数的本质就是数组;
- 可变参数可以和普通参数类型的参数一起放在形参列表,但必须保证可变参数放在最后;
- 一个形参列表中只能出现一个可变参数。
- 注意:
4.作用域(scope)
-
注意:
-
属性和局部变量可以重名,访问时遵循就近原则
public class VarScopeDetail{ public static void main(String arr[]){ Person p1 = new Person(); p1.say; } } class Person{ String name = "jack"; //局部变量 public void say(){ String name = "king"; //属性 System.out.println("say() name = " + name); //就近原则,输出 king } }
-
在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名
-
属性生命周期长,伴随着对象的创建而创建,伴随着对象的死亡而死亡。局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡。
-
作用域范围不同:
- 全局变量属性:可以被本类使用,或者其他类使用(通过对象调用)
- 局部变量:只能在本类中对应的方法中使用
-
修饰符不同
- 全局变量/属性可以加修饰符
- 局部变量不能加修饰符
-
5.构造器/构造方法(constructor)
-
需求:前面我们创建人类对象时,都是先把一个对象创建好后,再给他的年龄和姓名赋值,如果现在我们要在船舰对象时,就直接指定这个对象的年龄和姓名,和该怎么做?这时就需要用到构造器。
-
基本语法:[修饰符] 方法名(形参列表) { 方法体; }
-
说明:
- 构造器的修饰符可以默认
- 构造器没有返回值
- 方法名和类名必须一致
- 参数列表和成员方法一致
- 构造器的调用由系统完成
- 如果没有定义构造器,系统会自动给类生成一个无参构造器
- 一旦定义了自己的构造器,系统定义的构造器将被覆盖
-
作用:完成对对象的初始化
-
入门:
-
需求实现:
public class Constructor01{ public static void main(String arr[]){ Person p1 = new Person("Smith",80); //自动调用构造方法(已经完成对象的属性的创建和初始化 ) } } class Peerson{ String name; int age; //构造器 public Person(String pName, int pAge){ name = pName; age = pAge; } }
-
-
流程分析
- 加载Person类信息(Person.class),只会加载一次
- 在堆中分配空间(地址)
- 完成对象的初始化
- 3.1 默认初始化 age = 0 name = null
- 3.2 显式初始化 age = 20 name = “小倩”
- 在对象在堆中的地址返回给p(p 是对象名,也可以理解成是对象的引用)
6.this
-
问题引入
class Dog{ String name; int age; public Dog(String name , int age){ name = name; age = age; } public void info(){ System.out.println(name+"\t"+age+"\t"); //输出的是属性,而上面构造器中是给局部变量赋值,故而输出为空 } }
-
入门
- Java虚拟机会给每个对象分配 this,代表当前对象。坦白来讲,要明白 this 不是一件简单的事情
class Dog{ String name; int age; public Dog(String name , int age){ this.name = name; this.age = age; //this.XXXX 指的是对象的属性 } public void info(){ System.out.println(name+"\t"+age+"\t"); //此时就会有值输出了,当前对象的属性得到了赋值 } }
- 每一个对象都有默认的this,并且this指向这个对象本身
- hashCode():这是一个将一个对象地址转换成10进制数字的方法(特点)
- 注意:
public static void main(String arr[]){ Dog dog1 = new Dog("大壮",3); System.out.println("dog1的hashCode=" + dog1.hashCode()); Dog dog2 = new Dog("大黄",2); System.out.println("dog2的hashCode=" + dog2.hashCode()); } class Dog{ String name; int age; public Dog(String name, int age){ this.name = name; this.age = age; System.out.println("this.hashCode() = "+this.hashCode); } public info(){ System.out.println(name+" "+age); } }
-
注意:
-
this关键字可以用来访问本类的属性、方法、构造器
-
this用于区分当前类的属性和局部变量
-
访问成员方法的语法:this.方法名(参数列表);
-
访问构造器的语法:this(参数列表); 注意只能在 构造器中使用(即只能再构造器中访问另外一个构造器)
此时这条访问构造器的语句必须放在第一条去执行
class T{ public T(){ this("jack",100); System.out.println("T() 构造器"); //这里去访问 T(String name, int age) 构造器 //this("jack",100); !!!错误 } public T(String name; int age){ System.out.println("这里是构造器T(String name, int age)"); } }
-
this不能在类定义的外部使用,只能再类定义的方法中使用
-