面向对象是java语言的特点之一,因本章内容点比较多分为上下两篇,下篇包含内容为:
1. Overload 重载方法解析
2. 可变参数(认识和了解细节 ):method(int... param){ 方法体}
3. 作用域(Scope)
4. 构造器 (*)
5. This
用法
6. 本章测试题
Java语言学习笔记-【07面向对象、类与对象、成员方法、作用域、this等详解(上)】地址:
https://blog.youkuaiyun.com/qq_67947670/article/details/146403957
现开始进行javaSE复盘总结知识点,希望可以给java基础薄弱的友友提供一点参考,一起学习Java初级知识,共同进步,打好底层逻辑基础,爱上Java编程❤️❤️❤️
(本文章中知识点如有不同的见解,欢迎评论区留言)
第7章、面向对象编程基础【上】
1、类与对象
1、类与对象引出
如果使用现有知识解决出现的问题:
- 使用单独变量解决: 不利于数据的管理(你把一只猫的信息拆解)
- 使用数组解决
- 数据类型体现不出来
- 只能通过[下标]获取信息,造成变量名字和内容的对应关系不确定
- 不能体现猫的行为
Java设计者 引入 类与对象(OOP), 根本原因就是现有的技术不能完美的解决新的需求.
2、类与对象概述
一个程序就是一个世界,有很多事物( 对象[属性 , 行为] )
2.1、类与对象的关系示意图
3、类与对象快速入门
java特性有:封装、继承、多态
一般实体类独立放在一个java文件中,体现java的封装特性
//定义一个猫类
class Cat {
String name;
int age;
String color;
}
public class Object01{
public static void main(String[] args){
//使用面向对象的方式来解决养猫问题
//使用OOP main那个解决
//实例化一只猫[创建一只猫对象]
//1、new Cat() 创建一只猫
//2、Cat cat = new Cat();把创建的猫赋给 cat1
//3、cat1 就是一个对象(猫对象)
Cat cat1 = new Cat();
cat1.name = "波斯";
cat1.age = 3;
cat1.color = "花色";
//创建了第二只猫, 并赋给 cat2
//cat2 也是一个对象(猫对象)
Cat cat2 = new Cat();
cat2.name = "招财";
cat2.age = 3;
cat2.color = "白色";
//怎么访问对象的属性呢?
System.out.println("第一只猫信息" + cat1.name + " " +
cat1.age + " " + cat1.color);
System.out.println("第二只猫信息" + cat2.name + " " +
cat2.age + " " + cat2.color);
}
}
可以在Cat类中添加Cat的其他属性
3.1、类与对象的区别和联系
4、类和对象的内存示意图
这里简单说一下JVM虚拟机内存图流程
1、当创建对象时 即,new Cat();会在队中开辟一块空间,让栈中的对象指向堆中的地址
2、倘若对象中有基本数据类型会在队中创建一个常量池进行存储,String类型的数据则会在方法区中的常量池中进行开辟空间,再让队中的变量指向方法区中常量池的地址
3、如果JVM检测不到有数据被使用,那么JVM会启动垃圾回收机制,自动回收没有使用的资源
String name; //属性,成员变量, 字段 field
属性可以是基本数据类型 , 也可以是引用数据类型(对象 , 数组)
5、注意事项和细节说明Detail
四种访问修饰符:public 、 protected、默认(void)、private
修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
默认void | ✅ | ✅ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ |
*一个class代表一个类
- 有继承关系就是子类
- 是否在同一个package目录下,表示是否在同包下
全局变量自动赋值
public class PropertiesDetail{
public static void main(String[] args){
//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("\n当前这个人的信息:");
System.out.println(" age = " + p1.age + " name = " +
p1.name + "s al = " + p1.sal + " isPassword = " + p1.isPasswword);
}
}
//定义一个猫类
class Person {
//四个属性
int age;
String name;
double sal;
boolean isPasswword;
}
这里值得注意的是在全局创建的变量会自动进行赋值,如果是局部定义的变量需要进行初始化才能使用
6、创建&访问 对象
6.1、创建对象
两种创建方法
- 先声明再创建
Cat cat;//声明对象cat
cat = new Cat();
(两种方式内存中的区别)—>空的–>赋予地址
- 直接创建
Cat cat = new Cat();
直接赋予地址
6.2、访问对象
7、内存分配机制(*)
7.1、代码实现
只要有数据就会有地址
类传递时是地址传递
复习地址传递,即引用数据类型:类、接口、数组
代码 + 内存示意图展示
public class PropertiesDetail02{
public static void main(String[] args){
Person p1 = new Person();
p1.age = 12;
p1.name = "lisa";
Person p2 = p1;//把p1 赋给了p2 ,让p2指向 p1,这里传递的是地址
System.out.println(p2.age);
p2.name = "张三";
System.out.println("p1的名字为:" + p1.name);
}
}
//定义一个猫类
class Person {
//四个属性
int age;
String name;
double sal;
boolean isPasswword;
}
内存图 :
7.2、结构分析
- 先加载Person类信息(属性和方法信息 , 只会加载一次)
- 在堆中分配空间,进行默认初始化 (看规则)
- 把地址赋给 p , p就指向对象
- 进行指定初始化 , 比如 p.name = “jack” p.age = 12;
7.3、判断b = null
根据内存图进行分析这里是将地址进行传递,如果在方法区中让对象指向null,只会让方法中的对象指向堆内存的线断开,不会影响main中的对象指向
- 代码实现:
public class PropertiesDetail03{
public static void main(String[] args){
Person p1 = new Person();
p1.age = 12;
p1.name = "lisa";
Person p2;
p2 = p1;
System.out.println(p2.age);
p2 = null;
System.out.println("p1的名字为:" + p1.name);
System.out.println("p2的名字为:" + p2.name);//此时p2为空,没有数据,故会报错
}
}
//定义一个猫类
class Person {
//四个属性
int age;
String name;
double sal;
boolean isPasswword;
}
此时的main方法中的对象仍然存在,但是p2被复制的对象断开了连接,即再次访问时成为空指针异常
2、成员方法
2.1、基本介绍 & 快速入门
2.1.1、基本介绍
代码实现 :
//Method
public class Method{
public static void main(String[] args){
//方法使用
//1、方法写好后, 如果不去调用, 不会输出
//2、先创建对象, 然后调用方法即可
Person p1 = new Person();
p1.speak();//调用方法
}
}
//定义一个猫类
class Person {
//四个属性
int age;
String name;
//方法(成员方法)
//添加speak ,成员方法, 输出 “ 我是一个好人”
//1、public 表示方法是公开的
//2、void : 表示方法是没有返回值
//3、speak(): speak是方法名, ()星灿烈表
//4、{}方法体, 可以写我们要执行的代码
//5、System.out.println("我是一个好人");表示我们的方法就是输出 一句话
public void speak(){
System.out.println("我是一个好人");
}
}
输出 : 我是一个好人
2.1.2、快速入门
1、public 表示方法是公开的
2、void : 表示方法是没有返回值
3、speak(): speak是方法名, ()形参列表
4、{} 方法体, 可以写我们要执行的代码
5、System.out.println(“我是一个好人”); 表示我们的方法就是输出 一句话
//Method
public class Method{
public static void main(String[] args){
//方法使用
//1、方法写好后, 如果不去调用, 不会输出
//2、先创建对象, 然后调用方法即可
Person p1 = new Person();
p1.speak();//调用方法
p1.cal01();
p1.cal02(10);
//调用getSum方法,num1 = 10; num2 = 20
int returnRes = p1.getSum(10,20);
System.out.println("returnRes的值为:" + returnRes);
}
}
//定义一个猫类
class Person {
//四个属性
int age;
String name;
//方法(成员方法)
//添加speak ,成员方法, 输出 “ 我是一个好人”
//1、public 表示方法是公开的
//2、void : 表示方法是没有返回值
//3、speak(): speak是方法名, ()形参列表
//4、{}方法体, 可以写我们要执行的代码
//5、System.out.println("我是一个好人");表示我们的方法就是输出 一句话
public void speak(){
System.out.println("我是一个好人");
}
//添加 cal01 成员方法,可以计算从 1+..+1000 的结果
public void cal01() {
int sum = 0;
for (int i = 1; i <= 1000; i++ ) {
sum += i;
}
System.out.println("cal01中sum的值:" + sum);
}
//添加 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的值:" + sum);
}
//添加 getSum 成员方法,可以计算两个数的和
//老韩解读
//1. public 表示方法是公开的
//2. int :表示方法执行后,返回一个 int 值
//3. getSum 方法名
//4. (int num1, int num2) 形参列表,2 个形参,可以接收用户传入的两个数
//5. return res; 表示把 res 的值, 返回
public int getSum (int num1,int num2){
int res = num1 + num2;
return res;
}
}
2.2、方法调用机制原理图(*)
tips : 返回以后,新开辟的栈就会被销毁,就没有了
这里是重点,想要了解清楚JVM的执行流程就要配合代码一块思考
2.3、方法的妙处
如果使用原始方法则重复使用多个for循环,使用方法就只需要调用方法即可,简化代码.强化逻辑
//Method02
public class Method02{
public static void main(String[] args){
MyTools tool = new MyTools();
int[][] map = {{1,2,2},{2,3,3},{4,4,5}};
tool.printArr(map);
}
}
//把输出的功能, 写到一个类的方法中, 然后调用该方法即可
class MyTools {
//方法, 接收一个二维数组
public void printArr(int[][] map){
//对传入的map数组进行遍历输出
for (int i = 0;i < map.length ;i++ ) {
for (int j = 0;j < map[i].length ;j++ ) {
System.out.print( map[i][j] + "\t");
}
System.out.println();
}
}
}
2.4、方法使用
2.4.1、方法的定义
2.4.2、方法的细节Detail
一个方法只能有一个返回值,思考:如果需要多个返回值怎么处理?
答 : 可以使用数组处理,返回值可以是数组或者对象
//MethodDetail
public class MethodDetail{
public static void main(String[] args){
Aa a = new Aa();
int[] res = a.getSumAndSub(4 , 5);
System.out.println("和:" + res[0]);
System.out.println("差:" + res[1]);
}
}
class Aa {
public int[] getSumAndSub(int n1, int n2){
int[] resArr = new int[2];
resArr[0] = n1 + n2;
resArr[1] = n1 - n2;
return resArr;
}
}
提示 : 在实际工作中、我们的方法都是为了完成某个功能,所以方法名要有一定含义,最好是 见名知意
在同类中调用方法可以直接调用
注意return num % 2 != 0 ;
--> 如果是就返回true,不是返回false
2.4.3、方法练习题
2.4.3.1、练习1、
动态打印图形:
(这里和之前的二维数组很像,i表示第几层;j 表示第 i 层有 j 个图案)
/*
####
####
####
####
*/
//MethodExercise
public class MethodExercise{
public static void main(String[] args){
AA a = new AA();
a.printChar(3,4,'0');
}
}
class AA {
public int[] getSumAndSub(int n1, int n2){
int[] resArr = new int[2];
resArr[0] = n1 + n2;
resArr[1] = n1 - n2;
return resArr;
}
//练习1、
//根据行、列、字符打印 对应行数和列数的字符,
//比如:行:4,列:4,字符#,则打印相应的效果
/*
####
####
####
####
*/
//思路
//1. 方法的返回类型 void
//2. 方法的名字 print
//3. 方法的形参 (int row, int col, char c)
//4. 方法体 , 循环
public void printChar(int row ,int column , char c){
for (int i = 0; i < row ;i++ ) {
for (int j = 0;j < column ;j++ ) {
System.out.print(c);
}
System.out.println();
}
}
}
3、成员方法传参机制
3.1、方法传参机制
3.1.1、方法穿参机制01(基本数据类型)
引用参数中的参数发生变化 , 原main方法中的数据不变
演示:
基本数据类型在方法中赋值是值类型,而不是地址类型
不影响主方法中原来变量的数值大小的
//Parameter01
public class Parameter01{
public static void main(String[] args){
int a = 10;
int b = 20;
//创建AA对象 名字 obj
AA obj = new AA();
obj.swap(a, b);
System.out.println("\nmain主方法中的值\n a = " + a + " b = " + b); // a = 10,b = 20;
}
}
class AA {
public void swap(int a, int b) {
System.out.println("\na和b交换前的值:\n a = " + a + " b = " + b); // a = 10,b = 20;
int tmp = a;
a = b;
b = tmp;
System.out.println("\na和b交换后的值:\n a = " + a + " b = " + b); //a = 20;b = 10;
}
}
3.1.2、方法传参机制02(引用数据类型)
基本数据类型是直接在栈中开辟空间,栈内存是县城私有的,访问速度快,生命周期短
对象本身存储在堆内存中。堆内存是进程共享的,主要存储创建的对象。
引用数据类型传递时是传地址 , 方法参数改变, 主方法中的数据也改变mian
数组和对象(类)—》引用数据类型
数组传递内存图:
// MethodParameter01
public class MethodParameter01{
public static void main(String[] args){
B b = new B();
int[] arr = {1, 2, 3};
b.test100(arr);//调用方法
System.out.println("main方法中的 arr数组");
//遍历数组
for (int i = 0;i < arr.length ;i++ ) {
System.out.print(arr[i] + "\t");
}
System.out.println();
}
}
class B{
public void test100(int[] arr){
arr[0] = 200;//修改元素
//遍历数组
System.out.println(" test100的 arr数组");
for (int i = 0; i < arr.length ;i++ ) {
System.out.print(arr[i] + "\t");
}
System.out.println();
}
}
对象传递内存图:
// MethodParameter01
public class MethodParameter01{
public static void main(String[] args){
B b = new B();
//演示对象的传递机制
Person person = new Person();
person.age = 10;
b.test200(person);
System.out.println("main 的p.age = " + person.age);//10000
}
}
class Person{
int age;
String name;
}
class B{
public void test200(Person person){
person.age = 10000;//修改对象属性
}
}
3.1.3、方法传参机制案例
题一 : 判断
p.age
的值
public static void main(String[] args){
Person p = new Person();
p.age = 20;
B b = new B();
b.test200(p);
System.out.println("p的情况为:" + p.age);//20
}
class Person{
int age;
String name;
}
class B{
public void test200(Person p){
p = null;//p为空,此时test200开辟的栈空间中的对象链断开,
//不影响main方法中的值
}
题二 : 思考将会输出什么内容,认真思考这道题的过程 🤔
方法中new出来一个对象,就会在堆中重新创建一个对象,与main中的不同
// MethodParameter02
// MethodParameter02
public class MethodParameter02 {
public static void main(String[] args) {
B2 b = new B2();
Person05 person = new Person05();
person.age = 10;
person.name = "number1";
b.test200(person);
System.out.println("main 的person信息:" + person.name +
" " + person.age);//10000
}
}
class Person05 {
int age;
String name;
}
class B2 {
public void test200(Person05 p) {
//思考
p = new Person05();
p.name = "lisa";
p.age = 99;
//此时在test200中new了一个对象就会在堆中重新开辟一个空间
// 不会影响main中的信息
System.out.println("test200中person中的信息:" + p.name + " " + p.age);
}
}
内存示意图:
3.1.4、引用类型应用实例
问题2:
编写一个方法copyperson 可以复制一个person对象,返回复制的对象。克隆对象,
注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同
相当于数组的值传递问题
//MethodExercise02
public class MethodExercise02{
public static void main(String[] args){
Person p = new Person();
p.name = "meng";
p.age = 18;
myTools tool = new myTools();
Person p2 = tool.copyPerson(p);
//到此 p 和 p2是Person对象,但是是两个独立的对象,属性相同
System.out.println("p的属性 age =" + p.age);
System.out.println("p2的属性 age = " + p2.age);
//老师提示: 可以通过 对象比较看看是否为同一个对象
System.out.println(p == p2);//false
}
}
class Person{
String name;
int age;
}
class myTools{
//编写一个方法 copyPerson,可以复制一个 Person 对象,返回复制的对象。克隆对象,
//注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同
//
//编写方法的思路
//1. 方法的返回类型 Person
//2. 方法的名字 copyPerson
//3. 方法的形参 (Person p)
//4. 方法体, 创建一个新对象,并复制属性,返回即可
public Person copyPerson(Person p){
Person p2 = new Person();
p2.name = p.name;
p2.age = p.age;
return p2;
}
}
内存图: