以下笔记整理自B站UP主韩顺平【零基础 快速学Java】韩顺平 零基础30天学会Java课程
OOP
面向对象程序设计(Object Oriented Programming)
类就是数据类型,有属性和行为;对象是一个具体的实例
从类到对象,目前有几种说法:
创建一个对象
实例化一个对象
把类实例化
Java最大的特点就是面向对象
养猫问题(引出类与对象)
public class Object01 {
public static void main(String[] args) {
//使用OOP面向对象
//实例化一只猫【创建一只猫】
//new Cat() —— 创建一只猫
//把创建的猫赋给cat1 Cat cat1 = new cat();
Cat cat1 = new Cat();
cat1.name = "小白";
cat1.age = 18;
cat1.color = "白色";
//创建了第二只猫,赋给cat2
Cat cat2 = new Cat();
cat2.name = "小黄";
cat2.age = 4;
cat2.color = "黄色";
//怎么访问对象的属性
System.out.println("第1只猫的信息" + cat1.name + " " + cat1.age +
" " + cat1.color);
System.out.println("第2只猫的信息" + cat2.name + " " + cat2.age +
" " + cat2.color);
}
}
//使用面向对象的方式来解决养猫问题
//定义一个猫类Cat -> 自定义的数据类型
class Cat {
//属性
String name;
int age;
String color;
//后面还可以添加行为
}
对象内存布局
根据数据类型的不同,存放的位置也不同
成员变量 = 属性 = field(字段)
属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)。比如我们前面定义猫类 的 int age 就是属性
属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;
访问修饰符:控制属性的访问范围(public protected private 默认)
properties:属性
创建对象
如果只是声明对象,那它还是空的(null),new之后才开辟空间有地址。
Java 内存的结构分析
1) 栈: 一般存放基本数据类型(局部变量)
2) 堆: 存放对象(Cat cat , 数组等)
3) 方法区:常量池(常量,比如字符串), 类加载信
类与对象练习
编译运行结果如下图:
成员方法
public class Method {
public static void main(String[] args) {
//方法使用
//先创建对象,然后调用方法
Person p1 = new Person();
p1.speak(); //调用speak方法
p1.cal01(); //调用cal01方法
p1.cal02(50); //调用cal02方法
int returnRes = p1.getSum(10, 20); //调用getSum方法
System.out.println("getSum方法返回的值" + returnRes);
}
}
class Person {
//属性
String name;
int age;
//方法(成员方法)
//添加speak成员方法,输出"我是一个好人"
//public 表示 方法是公开的
//void 表示 方法没有返回值
//speak() speak表示方法名 ()表示形参列表
//{} 方法体 可以写我们要执行的代码
public void speak() {
System.out.println("我是一个好人");
}
//添加cal01成员方法,可以计算从1+2+3+...+1000
public void cal01() {
int res = 0;
for( int i = 0; i <= 1000; i ++ ) {
res += i;
}
System.out.println("计算结果=" + res);
}
//添加 cal02 成员方法,该方法可以接收一个数 n,计算从 1+..+n 的结果
//(int n)形参列表,表示当前有一个形参n,可以接收用户的输入
public void cal02(int n) {
int res = 0;
for( int i = 0; i <= n; i ++ ) {
res += i;
}
System.out.println("计算结果=" + res);
}
//添加 getSum 成员方法,可以计算两个数的和
/*
public 表示 方法是公开的
int 表示方法执行后返回一个int值
getSum 方法名
*/
public int getSum(int num1, int num2) {
int res = num1 + num2;
return res;
}
}
方法的调用机制原理【重点】
成员方法的定义
注意事项
访问修饰符 (作用是控制 方法使用的范围),如果不写默认访问,[有四种: public, protected, 默认, private]
一个方法最多有一个返回值 【如何返回多个结果?返回数组】
如果方法是 void,则方法体中可以没有 return 语句,或者 只写 return ;
方法不能嵌套定义
同一个类中的方法调用,直接调用即可,如下图
跨类中的方法A类调用B类方法,需要通过对象名调用
跨类的方法调用和方法的访问修饰符相关
parameter:参数
基本数据类型的传参机制
基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参【参考方法的调用机制原理图】
引用数据类型的传参机制【重点】
引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!
看下面代码加深理解【重难点】
public class MethodParameter {
public static void main(String[] args) {
B b = new B();
Person p = new Person();
p.name = "Jack";
p.age = 10;
b.text200(p);
System.out.println("main中p.age=" + p.age);
//输出main中p.age=10
}
}
class Person {
String name;
int age;
}
class B {
public void text200(Person p) {
//这里并没有影响main栈p对象,只是text200栈中的p置空了
p = null;
}
}
public class MethodParameter02 {
public static void main(String[] args) {
B b = new B();
Person p = new Person();
p.name = "jack";
p.age = 100;
b.text300(p);
System.out.println("main中p.age=" + p.age);
//输出结果:main中p.age=100
}
}
class Person {
String name;
int age;
}
class B {
public void text300(Person p) {
p = new Person(); //会在堆中创建一个新的p对象,text300栈中的p指向这个对象,main栈依然没变
p.name = "tom";
p.age = 10;
}
}
克隆对象
public class MethodExercise02 {
public static void main(String[] args) {
Person p = new Person();
p.name = "milan";
p.age = 10;
MyTools tool = new MyTools();
Person p2 = tool.copyPerson(p);
System.out.println("p的属性 age = " + p.age + " 名字=" + p.name);
System.out.println("p2的属性 age = " + p2.age + " 名字=" + p2.name);
}
}
class Person {
String name;
int age;
}
class MyTools {
public Person copyPerson(Person p) {
Person p2 = new Person();
p2.name = p.name;
p2.age = p.age;
return p2;
}
}
递归
recursion:递归
阶乘
factorial:阶乘
public class Recursion {
public static void main(String[] args) {
myTool tool = new myTool();
int res = tool.factorial(10);
System.out.println("res=" + res);
}
}
class myTool {
public int factorial(int n) {
if(n == 1)
return 1;
else
return factorial(n - 1) * n;
}
}
/*
10的阶乘3628800
*/
递归重要原则
斐波那契数列
fibonacci:斐波那契
import java.util.Scanner;
public class RecursionExercise01 {
public static void main(String[] args) {
Scanner myScanenr = new Scanner(System.in);
System.out.println("请输入一个整数:");
int n = myScanenr.nextInt();
myTool tool = new myTool();
int res = tool.Fibonacci(n);
System.out.println("第" + n + "项对应的斐波那契数为" + res);
}
}
class myTool {
public int Fibonacci(int n) {
if(n == 1 || n == 2)
return 1;
else
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
}
/*
斐波那契数列:1 1 2 3 5 8 13 21 ...
*/
猴子吃桃
第10天要吃的时候只剩1个桃子,问原来第1天有多少桃子?
public class RecursionExercise02 {
public static void main(String[] args) {
myTool tool = new myTool();
int res = tool.Monkey_peach(1);
System.out.println("res=" + res); //res=1534
}
}
class myTool {
public int Monkey_peach(int n) {
if(n == 10)
return 1;
else
return (Monkey_peach(n + 1) + 1) * 2;
}
}
老鼠出迷宫
import java.util.Scanner;
public class Maze {
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in);
System.out.println("请输入您要创建的地图大小:");
int row = myScanner.nextInt();
int column = myScanner.nextInt();
//创建一个地图,用二维数组表示
int[][] map = new int[row][column];
System.out.println("请绘制地图(每行以空格隔开,0表示没有障碍物,1表示有障碍物):");
//绘制地图
for( int i = 0; i < map.length; i ++ ) {
for(int j = 0; j < map[i].length; j ++ ) {
map[i][j] = myScanner.nextInt();
}
}
// //设置障碍物,1表示有障碍物,0表示没有障碍物
// for( int i = 0; i < 7; i ++ ) {
// map[0][i] = 1;
// map[7][i] = 1;
// }
// for( int j = 0; j < 8; j ++ ) {
// map[j][0] = 1;
// map[j][6] = 1;
// }
// map[3][1] = 1;
// map[3][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();
}
System.out.println("===================");
myTool tool = new myTool();
tool.findWay(map, 1, 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 myTool {
//对地图的说明:0表示没有障碍物,可以走;1表示有障碍物;2表示可以走;3表示走过,但是死路
//如果找到出口返回true,反之返回false
public boolean findWay(int[][] map, int i, int j) {
if(map[6][5] == 2){ // map[6][5] 为迷宫出口,说明已经找到出口
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 {
return false;
}
}
}
}
/*
地图1:
8 * 7
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
地图2:
8 * 7
1 1 1 1 1 1 1
1 0 0 0 0 0 1
1 1 1 1 1 0 1
1 1 1 1 0 0 1
1 0 0 0 0 1 1
1 1 1 0 0 0 1
1 1 1 0 0 0 1
1 1 1 1 1 1 1
*/
汉诺塔
public class HanoiTower {
public static void main(String[] args) {
myTool tool = new myTool();
tool.move(3, 'A', 'B', 'C');
}
}
class myTool {
//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 {
//如果有多个盘,可以看成两个,最下面的和上面的所有盘
//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);
}
}
}
/*
tool.move(3, 'A', 'B', 'C')
A->C
A->B
C->B
A->C
B->A
B->C
A->C
*/
重载
out是一个对象
注意事项和使用细节
可变参数
Variable parameter:可变参数
java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。就可以通过可变参数实现
public class VarParameter01 {
public static void main(String[] args) {
Method m = new Method();
int res = m.sum(1, 2, 3);
System.out.println("res=" + res);
}
}
class Method {
//可以计算2个数的和,3个数的和,4个数的和,...
//可以使用方法重载,但较麻烦
//使用可变参数优化
/*
1.int...表示接收的是可变参数,类型是int,即可以接收多个int(0~)
2.使用可变参数时,可以当做数组来使用,即nums可以当做数组
3.遍历nums,求和即可
*/
public int sum(int... nums) {
System.out.println("接收的参数个数=" + nums.length);
int res = 0;
for( int i = 0; i < nums.length; i ++ ) {
res += nums[i];
}
return res;
}
}
注意事项和使用细节
作用域
scope:作用域
在java编程中,主要的变量就是属性(成员变量)和局部变量
我们说的局部变量一般是指在成员方法中定义的变量
全局变量(属性)作用域为整个类体,成员方法可以使用属性
局部变量:除了属性之外的其他变量,作用域为定义它的代码块
全局变量(属性)可以不赋值,直接使用,因为有默认值;局部变量必须赋值后才能使用,因为没有默认值
属性和局部变量可以重名,访问时遵循就近原则
在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁;局部变量生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁,即在一次方法调用过程中生效。
全局变量(属性)可以被本类使用,也可以被其他类使用(通过对象调用);局部变量只能在本类中对应的方法中使用
全局变量(属性)可以加修饰符;局部变量不可以加修饰符
构造器(构造方法)
构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:
1) 方法名和类名相同
2) 没有返回值
3) 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。
javap指令
练习题
在前面定义的 Person 类中添加两个构造器:
第一个无参构造器:利用构造器设置所有人的 age 属性初始值都为 18
第二个带 pName 和 pAge 两个参数的构造器:使得每次创建 Person 对象的同时初始化对象的 age 属性值和 name 属性值。
分别使用不同的构造器,创建对象.
public class ConstructorExercise {
public static void main(String[] args) {
Person p1 = new Person();
System.out.println("p1.name=" + p1.name + " p1.age=" + p1.age);
Person p2 = new Person("jack", 20);
System.out.println("p2.name=" + p2.name + " p2.age=" + p2.age);
}
}
class Person {
String name;
int age;
//第一个无参构造器:利用构造器设置所有人的 age 属性初始值都为 18
public Person() {
age = 18;
}
//第二个带 pName 和 pAge 两个参数的构造器:使得每次创建 Person 对象的同时初始化对象的 age 属性值和 name 属性值。
public Person(String pName, int pAge) {
name = pName;
age = pAge;
}
}
/*
输出结果
p1.name=null p1.age=18
p2.name=jack p2.age=20
*/
面试题——对象创建流程分析【重点】
this
hashCode
返回该对象的哈希码值
该方法会针对不同的对象返回不同的整数,这一般是通过将该对象的内部地址转换成一个整数
来实现的
用hashCode方法来验证this关键字
public class This02 {
public static void main(String[] args) {
Dog dog1 = new Dog("jack", 20);
System.out.println("dog1的hashcode=" + dog1.hashCode());
dog1.info();
}
}
class Dog {
String name;
int age;
/*
如果我们构造器的形参,直接写成属性名,就比较好
但是出现了一个问题,根据变量的作用域原则
构造器的name / age就是局部变量,而不是属性
*/
public Dog(String name, int age) {
//this.name就是当前对象的属性name
this.name = name;
//this.age就是当前对象的属性age
this.age = age;
System.out.println("this.hashcode=" + this.hashCode());
}
public void info() { //这里的name和age还是属性
System.out.println(name + "\t" + age + "\t");
}
}
/*
hashcode:返回该对象的哈希码值
该方法会针对不同的对象返回不同的整数,这一般是通过将该对象的内部地址转换成一个整数
来实现的
输出结果:
this.hashcode=366712642
dog1的hashcode=366712642
*/
this小结
哪个对象调用,this就代表哪个对象
注意事项和使用细节
访问成员方法的语法:this.方法名(参数列表);
public class ThisDetail {
public static void main(String[] args) {
T t1 = new T();
t1.f2();
}
}
class T {
//细节:访问成员方法的语法:this.方法名(参数列表);
public void f1() {
System.out.println("f1() 方法...");
}
public void f2() {
System.out.println("f2() 方法...");
//调用本类的f1方法
//第一种方式
this.f1();
//第二种方式
f1();
//这两种方法是有区别的,在讲继承的时候会说
}
}
输出
f2() 方法...
f1() 方法...
f1() 方法...
访问构造器语法:this(参数列表),注意只能在构造器中使用(即只能在构造器中访问另外一个构造器)
public class ThisDetail {
public static void main(String[] args) {
// T t1 = new T();
// t1.f2();
T t2 = new T();
}
}
class T {
/*
访问构造器语法:this(参数列表),必须放在第一条语句,注意只能在构造器中使用
(即只能在构造器中访问另外一个构造器)
*/
public T() {
//访问T(String name, int age)构造器
this("jack", 20);
System.out.println("T() 构造器");
}
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();
//第二种方式
f1();
//这两种方法是有区别的,在讲继承的时候会说
}
}
输出
T(String name, int age) 构造器
T() 构造器
this不能在类定义的外部使用,只能在类定义的方法中使用
作业题
编写类A01,定义方法max,实现求某个double数组的最大值,并返回
/*
编写类A01,定义方法max,实现求某个double数组的最大值,并返回
*/
public class Homework01 {
public static void main(String[] args) {
double nums[] = {2.0, 0.2, 9.9, 3.25, 4.26};
A01 a01 = new A01();
Double res = a01.Findmax(nums);
if(res != null) {
System.out.println("最大的数为:");
System.out.println(res);
} else {
System.out.println("输入有误");
}
}
}
class A01 {
public Double Findmax(double... nums) {
if(nums != null && nums.length > 0) {
double MaxNum = nums[0];
for( int i = 1; i < nums.length; i ++ ) {
if(nums[i] > MaxNum) {
MaxNum = nums[i];
}
}
return MaxNum;
} else {
return null;
}
}
}
/*
如果double nums[] = {2.0, 0.2, 9.9, 3.25, 4.26};
输出结果:9.9
为增强代码健壮性
如果double nums[] = {};
如果double nums[] = null;
*/
成员方法的接收采用Double类的方式,既可以接收一个double类型的变量,也可以接收null
编写类A02,定义方法Find,实现查找某字符串是否在字符串数组中,并返回索引,如果找不到返回-1
/*
编写类A02,定义方法Find,实现查找某字符串是否在字符串数组中,并返回索引,如果找不到
返回-1
*/
public class Homework02 {
public static void main(String[] args) {
A02 a02 = new A02();
String[] str = {"jack", "tom", "merry", "jone"};
System.out.println(a02.Find("merry", str));
}
}
class A02 {
public int Find(String findstr, String[] str) {
for( int i = 0; i < str.length; i ++ ) {
if(findstr.equals(str[i]))
return i;
}
return -1;
}
}
编写类Book,定义方法updatePrice,实现更改某本书的价格,具体:如果价格>150,则更改为150;如果价格>100,则更改为100,否则不变
public class Homework03 {
public static void main(String[] args) {
book b = new book("悲惨世界", 128.5);
b.updatePrice();
b.info();
}
}
class book {
String name;
double price;
public book(String name, double price) {
this.name = name;
this.price = price;
}
public void updatePrice() {
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);
}
}
编写类A03,实现数组的复制功能copyArr,输入旧数组,返回一个新数组,元素和旧数组一样
public class Homework04 {
public static void main(String[] args) {
A03 a03 = new A03();
int[] arr = {1, 2, 3, 4, 5};
int[] newarr = a03.copyArr(arr);
for( int i = 0; i < newarr.length; i ++ ) {
System.out.print(newarr[i] + " ");
}
}
}
class A03 {
public int[] copyArr(int[] arr) {
int[] newarr = new int[arr.length];
for( int i = 0; i < arr.length; i ++ ) {
newarr[i] = arr[i];
}
return newarr;
}
}
定义一个圆类Circle,定义属性:半径,提供显示圆周长功能的方法,提供显示圆面积的方法
public class Homework05 {
public static void main(String[] args) {
Circle c = new Circle();
System.out.println(c.perimeter(3.0));
System.out.println(c.area(3.0));
}
}
class Circle {
double radius;
public double perimeter(double radius) {
return 2 * Math.PI * radius;
}
public double area(double radius) {
return Math.PI * radius * radius;
}
}
编程创建一个Cale计算类,在其中定义2个变量表示两个操作数,定义四个方法实现求和、查、乘、商(要求除数为0的话,并提示),并创建两个对象,分别测试
public class Homework06 {
public static void main(String[] args) {
Cale cale = new Cale(2, 0);
System.out.println("和=" + cale.add());
System.out.println("差=" + cale.minus());
System.out.println("乘=" + cale.mul());
Double Divres = cale.div();
if(Divres == null) {
System.out.println("除数不能为0");
} else {
System.out.println("除=" + cale.div());
}
}
}
class Cale {
double num1;
double num2;
public Cale(double num1, double num2) {
this.num1 = num1;
this.num2 = num2;
}
public double add() {
return num1 + num2;
}
public double minus() {
return num1 - num2;
}
public double mul() {
return num1 * num2;
}
public Double div() {
if(num2 == 0)
return null;
else
return num1 / num2;
}
}
阅读以下代码,分析输出结果
10 9 10