AI智能
改变未来

Java面向对象基础


类与对象

引入

单独变量解决:不利于数据的管理(把一只猫的信息拆解了,当信息量庞大时更加的麻烦)

//第一只猫信息string cat1Name = "小白"int cat1Age = 3;string cat1Color = "白色"//第二只猫信息string cat1Name = "小红"int cat1Age = 3;string cat1Color = "红色"n只猫.......

数组解决:

(1)数据类型体现不出来

(2)只能通过下标获取信息,造成变量名和内容的关系不对应

(3)猫的行为体现不出来(方法):如猫喜欢吃鱼这一行为体现不出来

String cat1[] = {"小白","3","白色"};String cat1[] = {"小小红","3","红色"}

引入类与对象的原因:现有的技术不能完美的解决新的要求

类与对象

类分为两部分:变量的声明(属性)和方法的定义

【类的定义格式】

class 类名 {类体内容}

属性概念

// 创建一个猫类——自定义的数据类型Class Cat {// 属性/成员变量String name; // 姓名int age; // 年龄String color //颜色String[] master;// 属性可以是基本数据类型,也可以是是引用类型(数组,对象)// 行为}

1.从概念上或叫法上看:成员变量 = 属性 = field(字段)

2.属性是类的一个组成部分,一般是基本数据类型,也可以是引用类型(对象,数组)

属性注意细节

  • 属性的定义同语法同变量,示例:访问修饰符 属性类型 属性名;

    访问修饰符:public、private、protected、默认——控制属性的访问范围

  • 属性的定义类型可以是任意类型,包含基本类型和引用类型

  • 如果属性不赋值,有默认值,规则和数组一致:

    boolean初始值:falseint初始值:0short初始值:0float初始值:0.0double初始值:0.0char初始值: \\u0000long初始值:0byte初始值:0Value初始值:null

    【参考代码】

    public static void main(String[] args) {Person p1 = new Person();System.out.println(p1.name);//nullSystem.out.println(p1.age);//0System.out.println(p1.sal);//0.0System.out.println(p1.isPass);//false}static class Person {String name;int age;double sal;boolean isPass;....}

对象

创建对象

1. 先声明在创建:Cat cat ; // 声明对象cat = new Cat(); // 创建2. 直接创建Cat cat = new Cat();

访问属性

基本语法:

对象名.属性名;

如:cat.name;

用类建立多个属性(自定义数据类型),通过实例化对象管理多个属性

public static void main(String[] args) {// 创建Person对象// p1 是对象名(对象引用)// new Person() 创建的对象空间(数据) 才是真正的对象Person p1 = new Person();}static class Person {String name;int age;double sal;boolean isPass;....}
p1 是对象名(对象引用)new Person() 创建的对象空间(数据) 才是真正的对象就如:小明是人名 它 指向 人(本质——对象)

对象的内存与分配机制

【看个例子】

Person p1 = new Person();

p1.age = 10;

p1.name = "小明";

Person p2 = p1; // 把p1赋给p2,让p2指向p1

System.out.println(p2.age);

问p2.age = ? 并画出内存图

很显然最后p2.age = 10

Java内存结构的分析

  1. 栈:一般存储**基本数据类型(**局部变量)、调用方法时开辟新栈
  2. 堆:存放引用类型(对象,如Cat cat 、数组等)
  3. 方法区:常量池(常量,比如字符串),类加载信息

对象创建过程总结:

  1. 先加载类(Person类)信息(属性和方法信息,只会加载一次)
  2. 在堆中分配空间,进行默认初始化(看规则)
  3. 把地址赋给p,p就指向对象
  4. 进行指定初始化(如:p1.age = 10;)

【再看个例子】

类与对象的区别及联系

  • 类是抽象的,概念的,代表一类事物,比如猫类、人类……,即 它是数据类型
  • 对象是具体的,实际的,代表一个具体事物,即 是实例
  • 类是对象的模板,对象是类的一个个体,对应一个实例

成员方法

在某些情况下我们需要定义成员方法(简称方法),就如Person类中出了age、name等属性外,我们还想给人加一些行为,例如:说话、跑步、做计算等等行为,这时就要用成员方法才能完成。

成员方法定义

【语法格式】

                权限修饰符 返回值类型 方法名(形参列表) {

                  // 方法体

                  // 返回值

                }

             1. 形参列表:表示成员方法输入,getSum(int num1 , int num2)         2. 返回数据类型:表示成员方法输出,void表示没有返回值             3. 方法体:表示为实现某一功能的代码块         4. return语句不是必须的

【例子】

  1. 添加speak成员方法,输出我是一个好人
  2. 添加cal01成员方法,计算1+…1000结果
  3. 添加cal01成员方法,计算1+…n结果
  4. 添加getSum成员方法,计算两个数的和
public class detail {public static void main(String[] args) {Person p1 = new Person();p1.speak();int ans = p1.cal01();System.out.println(ans);int ans2 = p1.cal02(1000);System.out.println(ans2);int ans3 = p1.getSum(5, 5);System.out.println(ans3);}}class Person {String name;int age;public void speak() {   // public:该方法是公开的System.out.println("我是一个好人");}int cal01() {int sum = 0;for (int i = 1; i <= 1000 ; i++) {sum += i;}return sum;}int cal02(int n){int sum = 0;for (int i = 1; i <= n ; i++) {sum += i;}return sum;}int getSum(int a, int b){return a+b;}}

方法的调用机制

方法调用小结:

  1. 当程序执行到方法时,就会开辟一个独立的空间(栈空间)
  2. 当方法执行完毕,或者执行到return语句时,就会返回
  3. 返回到调用方法的地方
  4. 返回后,继续执行方法后的代码
  5. 当main栈执行完毕后,整个程序也就退出了

方法使用细节

【返回类型】

  1. 一个方法最多有一个返回值

    【如何返回多个结果】——返回数组

    public class detail {    public static void main(String[] args) {        AA a = new AA();        int[] res = a.getSumAndSub(3,2);        System.out.println("和为"+ res[0]);        System.out.println("差为"+ res[1]);    }}class AA {    public int[] getSumAndSub(int num1, int num2){        int[] resArr = new int[2];        resArr[0] = num1 + num2;        resArr[1] = num1 - num2;        return resArr;    }}
  2. 返回类型可以是任意类型,包括基本类型或引用类型(数组,对象)

  3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为

    return 值;

    而且要求返回值类型必须和return的值的类型一致或兼容

  4. 如果方法是void,则方法体中可以没有return语句,或者写成

    return ;

  5. 开发规范:方法名遵循驼峰命名法,getSum

【形参列表】

  1. 一个方法可以有0个参数,也可以有多个参数,中间用逗号隔开,比如getSum(int num1 , int num2)
  2. 参数类型可以是任意类型,包含基本类型或引用类型,比如printArr(int[] [] map)
  3. 调用带参数的方法时,一定对应着参数列表传入相同的类型或者兼容类型的参数
  4. 方法定义时的参数称为形式参数,即形参;方法调用时的传入参数为实际参数,即实参。形参和实参的类型要一致或兼容、个数顺序必须相同。

【方法调用细节】

  1. 同一个类中的方法调用:可以直接调用

    演示:A类 sayOk()调用print()

    public class detail {    public static void main(String[] args) {        A a = new A();        a.sayOk();    }}class A {    // 同一个类中的方法调用:直接调用即可    public void print(int n){        System.out.println("print()方法被调用 n=" + n);    }    public void sayOk(){ // sayOk()直接调用print()        print(10);        System.out.println("继续执行sayOk()方法");    }}
  2. 跨类中的方法A类调用B类方法:不能直接调用,需要通过对象名调用

    public class detail {public static void main(String[] args) {A a = new A();a.mi();}}class A {// 跨类调用方法public void mi(){// 创建B类对象,然后用对象调用B类方法即可System.out.println("mi()方法被调用");B b = new B();b.hi();System.out.println("mi()方法 继续执行");}}class B {public void hi(){System.out.println("B类中的 hi()被调用");}}
  3. 跨类的调用和方法的访问修饰符有关!

方法传参机制

基本数据类型传参机制:

【分析输出结果是什么】

public class MethodParameter01 {public static void main(String[] args) {int a = 10;int b = 20;// 创建AA对象AA obj = new AA();obj.swap(a,b); // 调用swap()System.out.println("a=" + a + " b=" + b); //?}}class AA {public void swap(int a, int b){System.out.println("\\na和b交换前的值\\na=" + a + "\\tb=" + b);// 完成 a 和 b 的交换int temp = a;a = b;b = temp;System.out.println("\\na和b交换后的值\\na=" + a + "\\tb=" + b);}}

输出结果:

a和b交换前的值a=10 b=20

a和b交换后的值a=20 b=10a=10 b=20

【结论】

基本数据类型,传递的是值(值拷贝),形参的改变不影响实参!

引用类型传参机制:

B类中编写一个test100方法,可以接收一个数组,在方法中修改该数组,看看原来的数组是否发生变化?

public class MethodParameter01 {    public static void main(String[] args) {        B b = new B();        int[] arr = {1,2,3};        b.test100(arr); // 调用方法        // 遍历数组        System.out.println("\\nmain方法里的 arr数组");        for (int i = 0; i < arr.length; i++) {            System.out.print(arr[i] + " ");        }            }}// B类中编写一个test100方法//可以接收一个数组,在方法中修改该数组,看看原来的数组是否发生变化?    class B {    public void test100(int[] arr){        arr[0] = 100; // 修改元素        // 遍历数组        System.out.println("test100的 arr数组");        for (int i = 0; i < arr.length; i++) {            System.out.print(arr[i] + " ");        }    }}

输出结果:

test100的 arr数组100 2 3main方法里的 arr数组100 2 3

【再看一个案例】

B类中编写一个test200()方法,可以接收一个Person(age,sal)对象,在方法中修改对象属性,看看原来对象是否有变化。

public class MethodParameter02 {public static void main(String[] args) {B b = new B();//  测试Person p = new Person();p.name = "jack";p.age = 10;b.test200(p);System.out.println("main里的 p.age=" + p.age); // 2000}}class Person {String name ;int age ;}class B {public void test200(Person p){p.age = 2000; // 修改对象属性}}

p=null和p = new Person()会对原来的对象有影响吗?

public class MethodParameter01 {public static void main(String[] args) {B b = new B();//  测试Person p = new Person();p.name = "jack";p.age = 10;b.test200(p);// 测试 : 如果test200 执行的是 p = null 下面的输出结果是?System.out.println("main里的 p.age=" + p.age);   // 10}}class Person {String name ;int age ;}class B {public void test200(Person p){//      	p.age = 2000; // 修改对象属性// 思考://        p = null;p = new Person();p.name = "tom";p.age = 99;}}

p=null情况分析:

p = new Person()….情况分析:

注:被闲置的对象最终会被当成垃圾被回收

总结:p在哪里调用它就造哪个栈里的p

【结论】

引用类型传递的是地址(传递也是值,但值是地址),可以通过形参影响实参!

方法重载(Overload)

方法重载:一个类中可以有多个方法具有相同的名字,但这些方法的参数必须不同,及或者是参数的个数不同,或者是参数的类型不同。(方法名必须相同,但要求形参列表不一致)

重载的好处:

  • 减轻了起名的麻烦
  • 减轻了记名的麻烦

使用细节:

  • 方法名:必须相同
  • 形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求
  • 返回类型:无要求

【示例】

下面的Area类中,getArea方法是一个重载方法

class Area {	float getArae(float r){	return 3.14f * r * r;	}	double getArae(float x, int y){	return x * y;	}	float getArae(int  x, float y){	return x * y;	}	double getArae(float  x, float y, float y){	return (x*x + y*y + z*z) * 2.0;	}}

下面的案例不是方法的重载而是方法的重复定义!——参数名无要求,同理返回类型也无要求

double getArae(int x, int y){	return x * y;	}	double getArae(int a1, int a2){	return x * y;	}

可变参数

Java中允许将同一个类中**多个同名同功能但参数个数不同的方法,封装成一个方法**

【基本语法】

访问修饰符 返回类型 方法名(数据类型...形参名){}

【示例】

可计算两个数的和 , 3个数的和 , 4个数的和….

public class VarParameter01 {public static void main(String[] args) {Hspmethod m = new Hspmethod();int ans = m.sum(1,2,3,4);System.out.println(ans);}}class Hspmethod {//    可计算两个数的和 , 3个数的和 , 4个数的和....//    可以使用方法重载//    public int sum(int n1, int n2){//        return n1 + n2;//    }//    public int sum(int n1, int n2, int n3){//        return n1 + n2 + n3;//    }//    public int sum(int n1, int n2, int n3, int n4){//        return n1 + n2 + n3 + n4;//    }//.....// 上面的三个方法名相同,功能相同,但参数不同 -> 可使用可变参数优化//解读://    1、 int ... 表示接受的是可变参数 , 类型是 int ,即可接收多个int(0~多)//	  2、 使用可变参数时,可以当作数组来使用 , 即 nums 可以当作数组public int sum(int... nums){int res = 0;for (int i = 0; i < nums.length; i++) {res += nums[i];}return res;}}

【可变参数细节】

1、 可变参数的实参可以为0个或任意多个2、 可变参数的实参可以为数组

3、可变参数的本质就是数组

4、可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后

5、一个形参列表中只能出现一个可变参数

public class VarParameter02 {public static void main(String[] args) {Hspmethod m = new Hspmethod();int[] arr = {1,2,3};m.f1(arr);}}class Hspmethod {//  可变参数的实参可以为数组public void f1(int... nums){System.out.println(nums.length); // 3}//  可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后public void f2(String str, int... nums){}//	一个形参列表中只能出现一个可变参数    下面的写法是错误的!public void f3(double... nums, int... nums){}}

作用域

注意事项和使用细节:

  1. 属性(全局变量)和局部变量可以重名,访问时遵循就近原则

  2. 在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名。

  3. 属性生命周期较长,伴随着对象的创建而创建,伴随着对的销毁而销毁。局部变量,生命周期较短,伴随着代码块的执行而创建,伴随着代码块的结束而销毁。即在一次方法的调用过程中。

public class VarParameter01 {public static void main(String[] args) {Person01 p1 = new Person01();/*属性生命周期较长,伴随着对象的创建而创建,伴随着对的销毁而销毁。局部变量,生命周期较短,伴随着代码块的执行而创建,伴随着代码块的结束而销毁。*/p1.say();// 当执行say()方法时 say()方法的局部变量比如name就会被创建,当name执行完毕后// name局部变量就销毁,但属性(全局变量)仍然可以使用}}class Person01 {String name = "tom";public void say(){// 细节:属性和局部变量可以同名,访问时遵循就近原则String name = "King";System.out.println("say() name=" + name);}public void hi(){String address = "北京";// String address = "上海"; // 错误:重复定义变量String name = "lwt";}}
  1. 作用域范围不同

    全局变量/属性:可以在本类中使用,也可以在其它类中使用**(通过对象调用)**

    public class VarParameter01 {public static void main(String[] args) {Person01 p1 = new Person01();T t1 = new T();t1.test(); // 第一种跨类访问对象的属性方式t1.test2(p1); // 第二种跨类访问对象的属性方式}}class Person01 {String name = "tom";public void say(){// 细节:属性和局部变量可以同名,访问时遵循就近原则String name = "King";System.out.println("say() name=" + name);}}class T{// 使用Person01类中的“tom”public void test(){Person01 p1 = new Person01();System.out.println(p1.name);// tom}public void test2(Person01 p){System.out.println(p.name);// tom}}

    局部变量:只能在本类中对应的方法中使用

  2. 修饰符不同

    全局变量/属性可以加属性

    局部变量不可以加属性

构造方法/构造器(construcor)

构造方法是一种特殊的成员方法,它的主要作用是完成新对象的初始化,节省代码。当创建对象的时候,其实就是在调用构造器。

idea快捷键:alt + insert

【语法格式】

修饰符  类名(传参){}

【特点】

  • 方法名必须和类名相同
  • 没有返回值,也不能写void
  • 在创建对象时,系统会自动调用该类的构造方法完成对对象的初始化。

构造器/构造方法使用细节

  1. 一个类可以定义多个不同的构造器,即构造方法的重载

    public class Details {public static void main(String[] args) {Person p = new Person("tom",18);// 第一个构造器Person p2 = new Person("jack");// 第二个构造器}}class Person {String name;int age;// 第一个构造器public Person(String pName, int pAge){name = pName;age = pAge;}// 第二个构造器(构造器也是一种方法,当然可以重载)public Person(String pName){name = pName;}}
  2. 构造器名必须和类名相同

  3. 没有返回值,也不能写void

  4. 构造器是完成对象的初始化,并不是创建对象

  5. 在创建对象时,系统会自动调用该类的构造方法。

  6. 如果我们没有定义构造器,系统会自动给类生成一个默认无参构造器(默认构造器),比如Car ()

    public class Car {//成员变量----属性String color;int speed;int seat = 5;// Java会自动赠送每一个类一个无参的构造方法//在创建对象的时候,自己调用方法public Car() { // 默认构造器System.out.println("你好,我是构造方法");}public static void main(String[] args) {Car c1 = new Car();//默认调用的是构造方法------你好,我是构造方法}}
  7. 一旦定义了自己的构造器,默认的构造器就被覆盖了,就不能再使用默认的无参构造器,除非显式定义一下,即:Person() (这点很重要)

【对象创建的流程分析】

class Person {int age = 90;String name;// 第一个构造器public Person(String pName, int pAge){age = pAge; // 给属性赋值name = pName;}}Person p = new Person("小明", 18);

  1. 加载 Person类信息(Person.class),只会加载一次
  2. 在堆中分配地址空间(地址)
  3. 完成对象初始化【3.1、 默认初始化 age=0 name=null 3.2、显式初始化 age = 90,name = null,3.3、构造器的初始化 age = 20 name = "小明" 】
  4. 在对象堆中的地址,返回给p(p是对象名,也可以理解成对象的引用)——完成最终的初始化后才将地址赋给p,因此构造器是用来初始对象而不是创建对象(对象早已在堆中创建了)

this

什么是this:

Java虚拟机会给每个对象分配this,代表当前对象。

Java虚拟机会给每个对象分配this,代表当前对象,因此this与创建的对象的地址是一样的!

public class Details {public static void main(String[] args) {Person p = new Person("tom",18);System.out.println("当前对象的hashcode值:" + p.hashCode());}}class Person {String name;int age;public Person(String name, int age){this.name = name;this.age = age;System.out.println("this所代表的hashcode值:" + this.hashCode());}}

输出结果:

this所代表的hashcode值:460141958当前对象的hashcode值:460141958

小结:简单地说,哪个对象调用,this就代表哪个对象。

【this关键字使用细节】

  • this关键字可以用来访问本类的属性、方法、构造器

    package Constructor;public class Details {public static void main(String[] args) {T t = new T();t.f3();}}class T {String name = "tom";int num = 100;// this关键字可以用来访问本类的属性public void f3() {//传统方式System.out.println("name= " + name +" " + "num= " + num);//也可以用thisSystem.out.println("name= " + this.name + " " + "num= " + this.num);}}
  • this用于区分当前类的属性(全局变量)和局部变量

    ​ 【主要是看有无局部变量】

    package Constructor;public class Details {public static void main(String[] args) {T t = new T();t.f3();}}class T {String name = "tom";int num = 100;// this用于区分当前类的属性(全局变量)和局部变量public void f3() {String name = "jack";int num = 999;//传统方式:先根据就近原则(作用域)来找,看方法体中有无局部变量System.out.println("name= " + name +" " + "num= " + num);// name= jack num= 999//this:明确访问当前对象的属性(全局变量)System.out.println("name= " + this.name + " " + "num= " + this.num);// name= tom num= 100}}
  • 访问成员方法的语法:this.方法名(参数列表)

    public class ThisDetails {public static void main(String[] args) {T t = new T();t.f2();}}class T {public void f1(){System.out.println("f1()方法...");}public void f2(){System.out.println("f2()方法...");// 调用本类f1// 第一种方式f1();// 第二种方式this.f1();}}输出结果:f2()方法...f1()方法...f1()方法...
  • 访问构造器的语法:this(参数列表);注意只能在构造器中使用(只能在构造器中去访问另外一个构造器,必须放在第一条语句)

    public class Details {public static void main(String[] args) {T t = new T();}}class T {/*注意: 访问构造器语法:this(参数列表);必须放置在第一条语句*/public T(){//在这里去访问 T(String name, int age)构造器this("tom",18);System.out.println("T() 构造器");}public T(String name, int age){System.out.println("T(String name, int age) 构造器");}}
  • this不能在类定义的外部使用,只能在类定义的方法中使用

    学习内容源自视频:b站韩顺平老师说Java

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Java面向对象基础