面向对象编程基础
此为笔者个人笔记,如有错误还请指出
//例子一
public class Test{
public static void main(String[] args){
Cat cat1 = new Cat();
cat1.name = "小白";
cat1.color = "white";
cat1.age = 2;
}
}
class Cat{
String name;
String color;
int age;
}
类信息只加载一次
属性与成员变量是一个意思
类的注意事项
创建对象的方式与访问方式
Cat cat = new Cat();
Cat cat;
cat = new Cat();
类与对象的内存分配机制
跟数组一样,拷贝时也是拷贝的地址,浅拷贝
public class Test{
public static void main(String[] args){
Person p1 = new Person();
p1.name = "mio";
p1.age = 2;
p1.height = 3.3333;
Person p2;
p2 = p1;
System.out.println(p2.name + "\t" + p2.age + "\t" + p2.height);
p2.height = 4.33;
System.out.println(p1.name + "\t" + p1.age + "\t" + p1.height);
p2 = null;
//System.out.println(p2.name + "\t" + p2.age + "\t" + p2.height);//仅p2指向了空,p1不改变,此行抛出异常
System.out.println(p1.name + "\t" + p1.age + "\t" + p1.height);
}
}
class Person{
String name;
double height;
int age;
}
成员方法
class Person{
String name;
double height;
int age;
public void Speak(){
System.out.println("I'm a good person");
}
public int cal01(int n = 1000){
int sum = 0;
for(int i = 1; i <= 1000; i++){
sum+=i;
}
return sum;
}
}
调用成员方法时会在栈空间创建一个栈,执行成员方法,成员方法执行结束返回后此栈消失,返回到调用的地方,返回后继续执行后续代码
成员方法的好处:提高代码复用性、将实现细节封装,供其他用户调用即可
成员方法的定义:
public 返回数据类型 方法名(参数列表) {
//方法体
//...
return 返回值;//不是必须,由返回数据类型决定void就不用了(或者只写return;)
}
方法的使用细节
-
访问修饰符(四种,控制访问权限)
-
方法名的命名遵循驼峰命名法,最好见名知意
-
一个方法最多有一个返回值
-
一个方法可以有一个或者多个参数,方法类型可以是任意类型
-
注意参数和传入类型的兼容性(传小不传大)
-
方法定义时的参数称为形式参数,调用时的参数称为形式参数
-
实参形参要顺序、个数、类型一致
-
方法体中不能嵌套定义方法
方法调用细节
练习题
public class Exercise{
public static void main(String[] args){
AA a = new AA();
a.oOrJ(39876212);
a.Print(34, 78, @);
int c=10, d=20;
a.Swap(c, d);//仍是10和20
}
}
class AA{
public boolean oOrJ(int a){
//偶true奇false
return a%2==0? true : false;
}
public void Print(int l, int r, char c){
for(int i = 0; i < l; i++){
for(int j = 0; j < r; j++){
System.out.print(c+"\t");
}
System.out.println();
}
}
public void Swap(int a, int b){
int temp = a;
a = b;
b = temp;
}
}
方法传参机制
parameter
调用方法时,会创建一个新栈,若在新栈中若不是引用类型的数据进行修改,此修改对调用处的两元素是没有效果的(形参无法影响实参)
若是数组、类等引用类型的数据,修改是有效的(传的是地址,可以通过形参影响实参)
public class Exercise{
public static void main(String[] args){
AA a = new AA();
Person p = new Person();
p.name="11";
p.height=12.3;
p.age=33;
a.changePerson(p);
System.out.println(p.age);
//输出33,置空的只是方法栈中的指针,方法栈中修改Person的属性的值才会对main里面的p有作用
}
}
class Person{
String name;
double height;
int age;
}
class AA{
public void changePerson(Person p){
p = null;
p = new Person();
//同样不改变,开辟了新的对象,与之前的没关系了
}
}
Exercise
public class Exercise{
public static void main(String[] args){
int[][] a = {{1, 2, 3, 4},
{2, 2, 3, 4},
{3, 3, 4, 3}};
MyTools myTools = new MyTools();
myTools.Print(a);
Person p = new Person();
p.name="11";
p.height=12.3;
p.age=33;
Person q = myTools.copyPerson(p);
System.out.println(p.age);
}
}
class Person{
String name;
double height;
int age;
}
class MyTools{
public void Print(int[][] a){
for(int[] i : a){
for(int j : i){
System.out.print(j+"\t");
}
System.out.println();
}
}
public Person copyPerson(Person wt){
Person ans = new Person();
ans.name = wt.name;
ans.height = wt.height;
ans.age = wt.age;
return ans;
}
}
方法递归调用
-
每执行一次方法,就创建一个新的受保护的独立空间
-
方法的局部变量是独立的,不会互相影响(每次递归中相同的变量名)
-
若使用的是引用类型的数据,则递归过程中会共享此引用类型数据
-
递归时先写出口,避免无限递归
-
遵守谁调用返回给谁的原则
Exercise
public class Exercise{
public static void main(String[] args){
MyTools m = new MyTools();
int ans = m.Fab(7);
System.out.println(ans);
//没找的是0,障碍物是1,找过的是2,走不通的路是3
int[][] mp = {{1,1,1,1,1,1,1},
{1,0,0,0,0,0,1},
{1,0,0,0,0,0,1},
{1,1,1,0,0,0,1},
{1,0,0,0,0,0,1},
{1,0,0,0,0,0,1},
{1,0,0,0,0,0,1},
{1,1,1,1,1,1,1}};
}
}
class MyTools{
//指引方向:下右上左
int[][] tws = {{1,0},
{0,1},
{-1,0},
{0,-1}};
//求出斐波那契数列的第n项
public int Fab(int n){
if(n == 1 || n == 2) return 1;
return Fab(n - 1) + Fab(n - 2);
}
//猴子吃桃问题 n 天剩一个桃子 n-1剩4个桃吃3个 n为第一天,返回有多少个桃子
public int Peach(int n){
if(n == 2) return 4;
//前一天的桃子数等于今天剩桃子数+1 * 2
return (Peach(n - 1) + 1) * 2;
}
//小老鼠走迷宫
public boolean findWay(int[][] mp, int x, int y){
int x1, y1;
if(mp[6][5] == 2) {
return true;
} else{
if(mp[x][y] == 0){
mp[x][y] = 2;//走过
for(int i = 0; i < 4; i++){
x1 = x + tws[i][0];
y1 = y + tws[i][1];
if(findWay(mp, x1, y1)){
return true;
}//找到就true,没找到就换个方向找(有墙或标记了不通)
Print(mp);//打印当前路线,便于观察
System.out.println("========================");
}
mp[x][y] == 3; //四个方向都寄了,标记此路不通
return false;//四个都没走通,向上级返回false
}else{ //mp[x][y]==1,2 障碍/走过
return false;
}
}
}
//打印二维数组
public void Print(int[][] a){
for(int[] i : a){
for(int j : i){
System.out.print(j+"\t");
}
System.out.println();
}
}
//汉诺塔问题,n个盘子,从a借助b移动到c
public void Hannuota(int n, char a, char b, char c){
//递归出口
if(n == 1){
System.out.println(a+"->"+c);
}
//先将n-1个借助c移动到b
Hannuota(n - 1, a, c, b);
//再第n个移动到c
System.out.println(a+"->"+c);
//再将b上的n-1个借助a移动到c
Hannuota(n - 1, b, a, c);
}
//八皇后问题
//思路:一个放在一行一列
//第二个放在二行一列,判断是否满足(一定要考虑同一条斜线上的!!!),不满足就把所有列放完
//依次进行每一个
//得到一个正确的后回溯到第一个皇后放第二列,继续执行后续步骤
//每个皇后的行都不变,只对列进行变化
//判断是否可以放置在此位置的方法:
private boolean isOk(int row, int target, int[] num){
for(int i == 0; i < num.length; i++){
//前面是横,后面是斜线
if(num[i] = target || Math.abs(row - i) == Math.abs(target - num[i])){
return false;
}
}
return true;
}
//打印棋盘
private void PrintQueens(int[] arr){
for(int i = 0; i <arr.length; i++){
for(int j = 0; j < arr.length; j++){
if(j != arr[i]){
System.out.print("*");
}else{
System.out.print("^");
}
}
System.out.println();
}
System.out.println("========");
}
//记录解决方法数
private long solves;
//主函数
public int Queens(int n){
solves = 0;
goQueens(0, new int[n]);
return solves;
}
//用于递归的函数
private void goQueens(int row, int[] ans){
if(row == ans.length){
solves++;
PrintQueens(ans);
return ;
}else{
for(int i = 0; i < ans.length; i++){
if(isOk(row, i, ans)){
ans[row] = i;
goQueens(row + 1, ans);
ans[row] = 0;
}
}
}
}
}
方法重载介绍
同一个类中函数名同但功能不同
好处:减轻的起名和记名的麻烦
使用细节
- 方法名必须相同
- 形参列表必须不同(可以类型与顺序不同) 即使返回值类型不同,参数相同也不是重载
- 返回类型可以相同也可以不同
可变参数的使用
Java允许将多个同名同功能但参数个数不同的方法封装成同一个方法
语法:访问修饰符 返回类型 方法名(数据类型… 形参名);
使用可变参数时,可以视为数组
public class Learn{
public static void main(String[] args){
MyTools m = new MyTools();
int ans = m.hecheng(2, 3, 5, 7, 9, 10);
System.out.println("和是"+ans);
}
}
class MyTools{
public int sum(int... nums){
int ans = 0;
for(int i = 0; i < nums.length; i++){
ans += nums[i];
}
return ans;
}
public int hecheng(int a1, int... nums){
int sums = sum(nums);
return a1 * sums;
}
}
注意事项和使用细节
-
可变参数的实参可以是0 or 多个
-
实参可以是数组
-
本质就是数组
-
可变参数和普通参数一起作为形参,可变参数必须保证在最后
-
一个形参列表中只能出现一个可变参数(万万不可多)
作用域
局部变量一般指成员方法中定义的变量
方法属性可以和局部变量同名,访问遵循就近原则
同一作用域中,两个局部变量不能重名
属性生命周期长,随对象的创建而创建,伴随着对象的死亡而死亡,局部变量生命周期短,随着代码块执行而创建,随着代码块结束而死亡
全局变量/属性可以加修饰符,局部变量不可以
全局变量可以被本类使用,局部变量只能在本类中对应的方法中使用
构造器(构造函数)
完成对新对象的初始化
方法名要和类名字一致
语法:
class MyTools{
public MyTools(){
System.out.println("正在使用构造器");
}
public int sum(int... nums){
int ans = 0;
for(int i = 0; i < nums.length; i++){
ans += nums[i];
}
return ans;
}
public int hecheng(int a1, int... nums){
int sums = sum(nums);
return a1 * sums;
}
}
注意
- 构造器的修饰符可以默认,也可以是public protected private
- 构造器没有返回值
- 方法名和类名必须一样
- 参数列表和成员方法的参数列表一样的规则
- 构造器的调用由系统完成
- 在创建对象的时候,系统会自动调用该类的构造器完成初始化
- 如果程序员没有自定义构造方法,系统会自动给类生成一个默认无参数的构造方法,可以使用javap反编译看一下
- 一旦定义了自己的构造器,默认构造器就失效了,除非显示定义一下 Person(){};
public class Learn{
public static void main(String[] args){
Person p1 = new Person("ylll", 19);
System.out.println(p1);
}
}
class Person{
String name;
int age;
public Person(String pName, int pAge){
name = pName;
age = pAge;
System.out.println("你好"+pAge+"岁的"+pName);
}
}
class Dog{
String name;
}
Exercise
public class Exercise{
public static void main(String[] args){
Person p1 = new Person();
Person p2 = new Person("yll", 19);
System.out.println("Hello! "+ p1.age+ " "+ p1.name);
System.out.println("Hello! "+ p2.age+ " "+ p2.name);
}
}
class Person{
String name;
int age;
public Person(String pName, int pAge){
name = pName;
age = pAge;
//System.out.println("你好"+pAge+"岁的"+pName);
}
public Person(){
age = 18;
}
}
对象创建流程分析
Person p = new Person("yll", 19);
class Person{
String name;
int age = 90;
public Person(String pName, int pAge){
name = pName;
age = pAge;
//System.out.println("你好"+pAge+"岁的"+pName);
}
public Person(){
age = 18;
}
}
先默认创建对象,而后再初始化,构造函数执行的是对象的初始化
- 1、加载Person类信息(Person.class),只会加载一次
- 2、在堆中分配空间(地址)
- 3、完成对象初始化(默认初始化null 0 -> 显式初始化null 90 -> 构造器初始化yll 19)
- 将对象在队中的地址返回对象名(或者叫对象的引用)
this关键字
JVM给每个对象分配this,代表当前对象,相当于一个指向自己的指针
为了将属性与成员方法进行区分,如果不加this,会把name和age视作局部变量
class Person{
String name;
int age = 90;
public Person(String name, int age){
//name = name;
//age = age;
this.name = name;
this.age = age;
}
}
可以通过hashCode()来判断是否是同一个值
public class Exercise{
public static void main(String[] args){
Person p = new Person("yll", 19);
System.out.println("p.hashCode()="+p.hashCode());
Person p1 = p;
System.out.println("p1.hashCode()="+p1.hashCode());
}
}
class Person{
String name;
int age = 90;
public Person(String name, int age){
//name = name;
//age = age;
this.name = name;
this.age = age;
System.out.println("this.hashCode()="+this.hashCode());
}
}
哪个对象调用,this就代表哪个对象
注意事项
- this用于区分类的属性和局部变量
- 访问成员方法:this.方法名()
- 访问构造器:this(参数列表) 注意只能在构造器中使用
- this不能在类定义外使用,只能在类定义的方法中使用
public class Exercise{
public static void main(String[] args){
}
}
class Person{
String name;
int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
public boolean compareTo(Person p){
return this.name.equals(p.name) && this.age == p.age;
}
}
Homework
public class Homework{
public static void main(String[] args){
MyTools m = new MyTools();
//1、
double[] d1 = {1.4,2.5,3254.21,53436.232,1.34,777.77,12345.6,2324.3142,46436.2};
double dmax = m.max(d1);
System.out.println(dmax);
//2、
char[] ch1 = {'1', '2', 'y', 'm', 'x', 'l', '9', '0'};
int index = m.find(ch1, 'y');//2
System.out.println("下标是:"+index);
//4、
int[] i1 = {13,3,35,65,7,3,34,432,13,6,45,7,5};
System.out.println("i1:");
for(int i: i1){
System.out.print(i+"\t");
}
System.out.println();
int[] i2 = m.copyArr(i1);
System.out.println("i2:");
for(int i: i2){
System.out.print(i+"\t");
}
System.out.println();
}
}
class MyTools{
public double max(double[] nums){
double max = nums[0];
for(double num: nums){
if(num > max) max = num;
}
return max;
}
public int find(char[] chs, char target){
int ans = -1;
for(int i = 0; i < chs.length; i++){
if(chs[i] == target){
ans = i;
break;
}
}
return ans;
}
public int[] copyArr(int[] old){
int[] newArr = new int[old.length];
for(int i = 0; i < old.length; i++){
newArr[i] = old[i];
}
return newArr;
}
}
public class Homework{
public static void main(String[] args){
Book b = new Book();
b.price = 151;
b.updatePrice();
System.out.println("原价151,现价"+b.price);
b.price = 111;
b.updatePrice();
System.out.println("原价111,现价"+b.price);
}
}
class Book{
double price;
public void updatePrice(){
if(price > 150) price = 150;
else if(price > 100) price = 100;
else ;
}
}
public class Homework{
public static void main(String[] args){
Dog d = new Dog("ww", "blue", 3);
d.show();
}
}
class Dog{
String name;
String color;
int age;
public Dog(String name, String color, int age){
this.name = name;
this.color = color;
this.age = age;
}
public void show(){
System.out.println(color+"色的小狗名叫"+name+";今年"+age+"岁了");
}
}