类和对象

类和对象

1. oop(object oriented programming):面向对象编程

​ 把构成问题的事物都抽象成类,事物的行为特征抽象类的方法,事物的属性特征抽象类的属性,定义成程序的一种数据类型,通过调用对象的行为来实现功能。

2. java三大特征

封装(encapsulation) 、继承(inheritance)、多态(polymorphism)

3. 类

​ 类是对象的抽象,对象是类的具体化。

//补充:java中的注释:

//这是一个单行注释

/*
这是一个
多行注释
*/

/**
*这是一个文档注释
*/

//写一个类
修饰符 class 类名{

}

public class Student{
	public String name = "hello";//类中成员变量的定义
	public void sayHello(){					//类中方法的定义
		System.out.println("hello hello");
	}
}
public static void main(String[] args){
	//声明一个Student类变量
	Student stu;
	//该变量的使用需要一个具体化的Student数据,也就是一个Student对象
	stu = new Student();
	
}

4.对象

​ 类是对象的抽象,对象是类的具体化。面向对象的编程思想中,一切皆对象。

public static void main{
	//创建一个对象
	Student stu = new Student();
	//调用对象的属性
	System.out.println(stu.name);
	//调用对象的方法
	stu.getName();
} 

this关键字:

​ this关键字是一个引用,指向当前对象,(用到哪个对象,指向哪个对象)也就是说this存储了当前对象的地址.如图:

this关键字的作用:

1. 在本类方法中调用该类的另一个成员方法或使用成员变量

public class Student{
	public String name = "张三";
	
	//用this区分局部变量和成员变量(当局部变量名和成员变量名一致时)
    //如果没有变量冲突是this.name可以写成name,是一样的
	public void setName(String name){
		System.out.println(name);//这个打印的是传进来的参数
		System.out.println(this.name)//这个打印的是成员变量那么,也就是张三
        this.name = name;
	}
	
	public void study(){
		System.out.println("我在学习");
	}
	public void work(){
		this.study();   //调用本类的成员方法
		System.out.println("我的其他工作");
	}
}

2.在本类构造方法中调用该类另一个构造方法

public class Student{
	public String name;
	
	public Student(){
		//调用一个参数的构造器,参数的类型是String
		this("tom");
	} 
	
	public Student(String name){
		this.name = name;
	}
}

tip:不能在普通方法调用构造方法,构造方法中的this语句必须放在非注释性语句第一行。

5. 引用

​ 什么是引用?

引用就是引用类型的变量,简称引用。该变量是一个对象地址,指向对 象。

public static void main(String[] args){
	//创建了一个对象,但没有引用
	new Student();
	
	//stu是一个引用,指向创建的Student类对象
	Student stu = new Student();
}

​ 如果没给对象起名字,也就是说该对象没有引用,那么这个对象能使用吗?

​ 答案是肯定的,没有引用的对象创建了在堆中同样分配内存,但是只能使用一次,因为他没有名字,无法在语法上表示,只能在创建时使用。

如:

public static void main(String[] args){
	System.out.println(new Student());
}

引用、对象、类之间的关系?

​ 打一个比喻吧,假设类是空调的图纸,对象就是实体的空调,而引用就是空调遥控器。

6. 内存中对象的创建是怎样的?

public static void main(String[] args){
	Student stu = new Student();
}

上述代码执行的内存变化:

  • 以上为jdk1.7内存模型,类文件从硬盘中加载到方法区后,执行类文件,
  • 找到程序执行的入口(main方法)开始运行
  • 在栈中main方法压栈,
  • 创建类型为Student的属性stu
  • 当new Student()时,在堆区中创建了Student对象并为它分配了一块内存,该内存地址为0x123,
  • Student stu = Student();就是把创建的内存块的地址赋值给stu。

7.方法

7.1方法的格式

修饰符 返回值 方法名(参数列表)throw 抛出异常类型{
	//方法体
}

7.2修饰符

修饰符有:public、protected、default(默认,不写出来)、private、static、final(前四个为访问控制修饰符),修饰符没有顺序之分。

public class Student{
	//在学生类中定义一个study方法
	public void study()throw Exception{
		System.out.println("我在学习");
	}
}

可以不写修饰符,不写时为默认,没有抛出异常时也可不写异常。剩下的都是一个方法的必备部分。

public class Student{
	//在学生类中定义一个study方法
	void study(){
		System.out.println("我在学习");
	}
}

7.3返回值

返回值类型和方法名都是必须写的,只有一种方法不用写返回值类型,那就是构造方法,返回的是本类。

当有返回值时,必须使用return返回一个值,不然编译报错。

public class Student{
	//在学生类中定义一个study方法
	String study(){
		System.out.println("我在学习");
		return "我在学习";//返回了一个String类型的值
	}
}

那么,返回值类型为void的方法可不可以存在return关键字呢?

​ 可以的,return有两个作用,一个是返回一个值,另一个是结束本方法。如:

public class Student{
	//在学生类中定义一个study方法
	void study(){
		System.out.println("我在学习");
		return;
	}
}

7.4方法名规范

​ 方法名符合标识符命名规范即可(可中文,但不推荐。起有意义的名字)

​ 推荐使用规范:方法名第一个单词首字母小写其余单词首字母大写。如:sayHello()。

7.5方法的执行

​ 上面都是方法定义,并没有被执行,方法需要被调用后才能够执行。如何去调用呢,是哪个类的方法就用哪个类的对象去调用(static方法同样可行,但不推荐)。

public class Student{
	//定义study方法
	public void study(){
		System.out.println("我在学习!!");
	}
	
	//程序执行入口main方法
	public static void main(String[] args){
		//方法的执行首先创建该方法的本类对象也就是Student类对象
		Student stu = new Student();
		//再用该对象调用方法
		stu.study();
	
	}	
}

7.6 方法的递归调用

什么是递归?

​ 简单的说,递归就是自己调自己,在代码中表现为方法的内部又调用了该方法。如:

public void test(){
	test();
}

如果只是这样,那会无限打开test方法形成一个死循环,编译不会报错,但代码运行表现为出现错误StackOverflowError,如下图:

这时,我们需要一个条件来退出这个递归序列,也就是执行到何时把方法关闭了,如:

int a = 1;
public void test(){
//...
	a++;
	if(a==10){
		return;
	}
	test();
//...
}

通过以上两段代码,我们可以看出:递归由递归头递归体组成

控制方法退出的条件为递归头,递归体就是自己调自己,调用本身这个方法。

例题:用递归计算1~100之和

public int f(int num){
	if(num==1){
		return 1;
	} 
	return num+f(num-1);
}

7.7 方法参数

7.7.1 参数列表类型
  • 无参
  • 一参
  • 多参
  • 可变参
public class Student{
	void study(){
		System.out.println("这是一个无参方法。");
	}
    
    void study(String s){
		System.out.println("这是一个一参方法。"+s);
        //+  在这里是一个连接符
	}
    
    void study(String s,String s){//多个参数间用逗号隔开
		System.out.println("这是一个多参方法。");
	}
    
    void study(String... s){//可变参的本质是一个数组
		System.out.println("这是一个可变参方法。");
	}
}
7.7.2参数传递

参数类型:

形参(形式参数):用来接收值,本身是没有值的

实参(实际参数):就是一个值

public class Student{
	public static void main(String[] args){
		Student stu = new Student();
		stu.study(1);//在这里1是一个实际的值,是一个 实参
	}
	public void study(int a){//这里的a没有特定的值,用来接收值给方法使用 ,所以这里是一个 形参
		System.out.println("我在学习一个数,这个数是:"+a);
	}
}

参数传递(把形参传给实参)的两种方式:

看的是传递的值的类型

值传递:对于基本数据类型来说参数的传递形式是把存储的值copy一份赋值给形参,也就是说在方法中改变这个值是不会对原来存储的值做出改变。

例子如下:

public class Test {
    //该方法中,改变参数当前的值
    public static void changeNum(int a) {
        a = 10;
    }

    public static void main(String[] args) {
        int a = 1;
        System.out.println("before: a = " + a);//传参之前,变量a的值
        changeNum(a);
        System.out.println("after: a = " + a);//传参之后,变量a的值
    }
}

引用传递:对于引用数据类型(在java中除了基本数据类型,剩下的就是引用数据类型)来说,参数传递传的是一个地址,传递之后,形参和实参指向的是同一片内存空间。也就是说形参和实参现在就像两个遥控器,可以控制同一个空调(地址)。

例子如下:

public class Test{
    //该方法中,改变引用s所指向对象的name属性值
    public static void changeName(Student s){
        s.name = "tom";
    } 
    public static void main(String[] args){
        Student s = new Student();
        System.out.println("before: name = "+s.name);//传参之前,引用s所指向对象的name属性值
        
        changeName(s);
        System.out.println("after: name = "+s.name);//传参之后,引用s所指向对象的name属性值
        
    }
}

7.8方法的重载

重载方法必须在同一个类中

重载方法方法名必须与原方法方法名一致

方法的重载只跟方法的参数列表有关,与方法的修饰符 、返回值类型、抛出的异常无关。

7.8.1方法重载情况

参数列表不同的情况有:

  • 参数的个数不同
  • 参数的顺序不同
  • 参数的类型不同

例如:

public class Test{
	//参数的个数不同
	public void test(){}
	
	public void test(int a){}
	public void test(String s1,String s2){}
	
	//参数的类型不同
	public void test(boolean flag){}
	public void test(double salary){}
	
	//参数的顺序不同
	public void test(int a,String s){}
	public void test(String s,int a){}
	
	//方法的参数可以相同,也可以不同
	public int test(int a,int b){
		return 0;
	}
}

7.8.2不构成方法重载的情况:
public class Test{
    //原方法
	public void test(int a,int b){}
    
	//方法不构成重载,编译报错,会提示方法已定义的    
	public void test(int b,int a){}
    
    //返回值类型不同不构成重载
	public int test(int a,int b){return 0;}
    
	//修饰符不同不构成重载
    private void test(int a,int b){}
}
7.8.3 方法重载的特殊情况:
public class Test{
	//方法重载
	public void test(int a,long b){}
	public void test(long b,int a){}
    
	public static void main(String[] args){
		Test t = new Test();
		t.test(1,1L);//调用到第一个方法
		t.test(1L,1);//调用到第二个方法
		t.test(1,1);//问题:这个会调用到哪一个test方法?
}
}

上述问题,编译报错

因为参数无法匹配方法时会尝试做类型转换,但是类型转换后两个方法同时匹配,所以编译报错。

public class Test {
	//重载方法1
	public void test(int a){}
	//重载方法2
	public void test(short a){}
    
	public static void main(String[] args){
		Test t = new Test();
		byte a = 1;
		t.test(a);//这里会调用第二个方法,也就是short类型参数的test方法
}
}

虽然byte类型数据,可以自动转换为short,也可以转换为int,但是short离byte“更近” 。

posted @ 2021-03-28 10:45  愿半生代码如一生好友  阅读(122)  评论(0)    收藏  举报