本文是学习韩顺平老师Java课程时整理的学习笔记,内容为个人理解与总结,如有错误欢迎指正。

类与对象(OOP)

类(自定义数据类型):猫的所有属性(name, age, color)和行为(run, cry, eat)

int是java提供的数据类型,对象值是100,200;

对象(实例):具体一只猫,创建对象(一个具体的实例)

int a = 100;
Cat c1 = new Cat();

类是抽象的概念的,是数据类型;对象是具体的实际的实例;类是对象的模板,对象是类的一个个体;

class Cat{         //定义一个类Cat,有哪些属性(成员变量)
		String name;
		int age;
		String color;
}
public class OopTest01 {
    public static void main(String[] args) {
		    Cat cat1 = new Cat();     //创建一个对象new Cat(),对象名是cat1
		    cat1.name = "小白";
		    cat1.age = 3;
		    cat1.color = "白色";
		    
		    Cat cat2 = new Cat();     //创建一个对象
		    cat2.name = "小花";
		    cat2.age = 10;
		    cat2.color = "花色";
		    //访问对象的属性
		    System.out.println("第二只猫的信息" + cat2.name + " " 
		    + cat2.age + " "+ cat2.color);
    }
}

类的内存形式

class类和String和数组一样,都是引用数据类型

属性 = 成员变量 ,对象的属性有默认值

class Car{                  //定义一个类Car
		protected String name;  //访问修饰符+属性类型+属性名
		double price;
		String color;
		String[] owner;         //可以是基本数据类型,引用数据类型
}
Car c1;
c1 = new Car();             //先声明再创建对象
Car c2 = new Car();         //直接创建
c1.price;                   //访问对象的属性

类和对象的内存分配机制

栈:存放局部变量,基本数据类型的值(a=10),引用类型的地址(数组arr的地址,对象p1的地址)

堆:存放new产生的对象(对象具体的值,数组的值)

方法区:常量池(如String的值”小明”),类加载信息

int a = 10;
int[] arr = new int[5];

//Person类包含名字,年龄
Person p1 = new Person(); 
//在方法区加载Person类信息,在堆中创建对象分配空间默认初始值
//在栈中创建引用变量p1,保存对象的地址
p1.age = 10;
p1.name = "小明";
Person p2 = p1;      //引用赋值,指向同一个对象

class Person{
		String name;
		int age;
}

创建成员方法

类 = 属性(成员变量)+ 行为(成员方法),成员方法简称方法

/*
访问修饰符 返回数据类型 方法名(形参列表){ //方法体
语句;
return 返回值;  //可以没有返回,void时return后不能有值
} */
//最多只有一个返回值(可以是数组),返回数据类型和return的值类型一致或兼容
public int[] getNum(int n1, int n2){
		int[] res = new int[2];
		res[0] = n1 + n2;
		res[1] = n1 - n2;
		return res;
}

方法可以多次调用,形参列表是方法的输入,方法体实现功能的代码块;

访问修饰符(作用是控制方法/属性的使用范围)有四种public,protected,包访问(默认),private;

同一个类中的方法调用:直接调用;跨类通过对象名调用;

public class Method01 {
    public static void main(String[] args) {
		    Person p1 = new Person();  //创建对象
		    p1.age = 10;
				p1.name = "小明";
				p1.speak();                //调用方法
				p1.cal01(10);              //调用方法,n=10
				int a = p1.getSum(10,20);  //调用方法n1=10,n2=20,返回值赋给a
				System.out.println("返回值" + a);
    }
}
class Person{           //Person类的属性name,age,方法speak,cal01,getSum
		String name;
		int age;
		//添加speak方法:public表示方法是公开的,void表示方法没有返回值
		//speak是方法名,()是形参列表,{}是方法体-具体要做的事
		public void speak(){     
				System.out.println("我是一个好人");
		}
		//n是形参,可以接收用户输入
		public void cal01(int n){
				int sum = 0;
				for(int i = 1; i <= n; i++){
						sum += i;
				}
				System.out.println("sum=" + sum);
		}
		//int表示方法有int类型的返回值,两个形参,返回res的值return跳出方法
		public int getSum(int n1,int n2){
				int res = n1 + n2;
				return res;
		}
}

**方法的调用机制**(getSum案例)非常重要!!

(图的一个小问题是main栈应该在下面,getSum栈空间在上面)

1、Method01和Person类信息加载进方法区(方法如何执行);

2、创建main方法的栈空间,在main栈执行语句new Person()在堆中创建对象,分配0x0011地址空间,成员变量赋默认值,并在main栈中创建局部变量p1,保存0x0011地址指向堆中的对象;

3、执行age和name赋值,10在堆中修改,name通过地址引用指向常量池中“小明”;

4、在main栈执行getSum方法调用语句,创建getSum栈空间,放入n1=10, n2=20,在getSum栈空间执行方法体得到res=30,并将值返回给main栈空间中的变量a=30,return后getSum栈空间立即销毁;

5、执行System.out输出,main方法结束,程序退出,然后main栈空间销毁,堆中的Person对象失去地址引用,成为可回收对象;

public class Method01 {
    public static void main(String[] args) {
		    Person p1 = new Person(); 
		    p1.age = 10;
				p1.name = "小明";
				int a = p1.getSum(10,20); 
				System.out.println("返回值" + a);
    }
}
class Person{   
		String name;
		int age;        
		public int getSum(int n1,int n2){
				int res = n1 + n2;
				return res;
		}
}

方法的传参机制

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

引用数据类型,传递的是地址,形参的改变影响实参;(类是引用数据类型)

public class Method03 {
    public static void main(String[] args) {
		    Person p = new Person();
		    p.name = "jack";
		    p.age = 10;
		    B b = new B();
		    b.test2(p);        //p的值是一个地址,指向Person对象
		    System.out.println("main的p.age=" + p.age);  //=100
		}
}
class Person{
		String name;
		int age;
}
class B{
		public void test2(Person p){  //test2方法传入一个地址p(数据类型是Person)
			  p.age = 100;              //根据地址修改属性的值
		}
}

递归调用

执行一个方法时,就创建一个新的栈空间;谁调用结果返回给谁;

public class Recursion01 {
    public static void main(String[] args) {
		    T t1 = new T();
		    t1.test01(4);          //每个递归创建新栈,返回输出n=2,n=3,n=4
		    int res = t1.factorial(5);
		    System.out.println("res=" + res);
    }
}
class T{
		public void test01(int n){    //递归案例1
				if(n > 2){
						test01(n - 1);     
				}
				System.out.println("n=" + n);
		}
		public int factorial(int n){  //递归案例2-阶乘
				if(n == 1){
						return 1;
				}else{
						return factorial(n - 1) * n;  //向上递归生成栈,向下返回值
				}
		}
}

方法重载Overload

同一个类中,允许同名方法存在,但要求形参列表不同,类型/顺序/个数不同;

形参名不同/返回值类型不同不能构成重载,会报错;

class Overload{
		public int calculate(int n1, int n2){   //正确应用重载
				return n1 + n2;
		}  
		public double calculate(int n1, double n2){
				return n1 + n2;
		}
		public double calculate(double n1, int n2){
				retrun n1 + n2;
		}
}

可变参数

java可以将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法,通过可变参数实现,接收任意个参数;(不常用)

public int sum(int... nums) {   //n个数的和,0-多个,nums当成数组
		int res = 0;
		for(int i = 0; i < nums.length; i++){
				res += nums[i];		
		}
		return res;
}

作用域

变量:全局变量(属性(成员变量))和局部变量(成员方法和代码块定义的)

局部变量的作用域只在对应方法中,全局变量(属性)作用域是整个类

public class VarScope {
    public static void main(String[] args) {
		    Person p1 = new Person();
		    Cat c1 = new Cat();
		    c1.test2(p1);
    }
}
class Cat{
		int age = 10;                //全局变量
		public void cry(){
				int n = 1;
				String name = "jack";    //局部变量
		}
		public void test2(Person p){    //跨类调用
				System.out.println(p.name);
		}
}
class Person{
		String name = "jack";
}

全局变量(属性)可以不赋值,有默认值;局部变量必须赋值,没有默认值;

全局和局部变量可以重名,就近访问;

全局变量也可以被其他类通过对象调用;

全局变量可以加修饰符,局部变量不可以;

构造器

构造器是类的一种特殊方法,实现对新对象的初始化

构造器没有返回值,方法名和类名相同(后面用this写)

public class Constructor01 {
    public static void main(String[] args) {
		    //new创建对象时,可以通过构造器初始化属性的值
				Person p1 = new Person("smith",80);
    }
}
class Person{
		String name;
		int age;
		public Person(String pname, int page){
				System.out.println("构造器被调用");
				name = pname;
				age = page;
		}
}

一个类可以有多个构造器(重载)

若没有定义构造器,系统会自动生成默认无参构造器

对象创建的流程分析(含构造器)

1、方法区加载Person类信息,只会加载一次;

2、堆中创建对象分配空间(地址);

3、对象初始化:首先默认初始化0,然后显式初始化(类的属性有赋值),最后构造器的初始化;

4、对象在堆中的地址返回给p;

class Person{
		String name;
		int age = 90;                  //堆中age的值变化:0-90-20
		Person(String n, int a){
				name = n;
				age = a;
		}
}
Person p = new Person("小倩",20);  //简化(实际写在main中)

this关键字

前面的构造器都要用新的变量接收初始化的值,变成this.age = age;

哪个对象调用,this就代表哪个对象;

class Dog{
		String name;
		int age;                  
		public Dog(String name, int age){
				this.name = name;       //表示当前对象的属性
				this.age = age;
		}
} 
Dog dog1 = new Dog("大壮", 3);  //简化(实际写在main中)
Dog dog2 = new Dog("大黄", 2);



//案例
public class this01 {
    public static void main(String[] args) {
        //通过构造器进行对象初始化
        Person p1 = new Person("jack", 20);
        Person p2 = new Person("smith", 30); 
        System.out.println("p1和p2比较结果" + p1.compareTo(p2));  //false
        //this代表调用对象p1,p代表传入的p2
    }
}

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;
    }
}

this可以访问本类的属性,方法,构造器;

this能够区分全局变量(属性)和局部变量this.age = age;

class T {
		//两个构造器
		public T(){  
				this("jack", 10);  //this只能在一个构造器中访问另一个构造器 
				System.out.println("无参构造器");
		}                      
		public T(String name, int age){
				System.out.println("有参构造器");
		}
		//两个方法
		public void f1(){
				System.out.println("f1()方法");
		}
		public void f2(){
				System.out.println("f2()方法");
				f1();         //两种方式都可以访问f1方法
				this.f1();    //this访问f1方法
		}
}

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐