java的类

  • 引言
  • 一、类的定义与引用
  • 1、类的定义
  • 2、类的引用
  • 二、类的初始化(加载)过程
  • 1、java程序执行流程
  • 2、java运行时内存结构
  • 方法区
  • 程序计算器
  • 本地方法栈
  • 三、类方法
  • 1. 实例方法
  • 2. 静态方法
  • 3. 构造方法
  • 4. 继承方法
  • 5. 重载方法
  • 6. 抽象方法
  • 7. final方法
  • 四、方法重载
  • 特点:
  • 五、类访问修饰符
  • 访问修饰符的作用范围:
  • 访问修饰符的继承性:
  • 尽量限制成员的访问权限:
  • 六、this和super
  • this
  • super
  • 总结:


引言

类这一模块介绍的是java类的定义与引用、类的初始化(加载)过程、构造器、类方法、方法重载、访问修饰、this和super

一、类的定义与引用

1、类的定义

在java中一个类通常的有如下内容:
包定义语句 package ……;
import语句 import ……;
class 类名{
成员变量;
构造方法;
成员方法;
}

package com.stydy.test;

import java.util.ArrayList;
import java.util.List;

public class Person{
    private  String name="菜鸡";
    private List<String> list=new ArrayList<>();
    public Person(){}

    public Person(String name){
        this.name=name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void saveName(String name){
        list.add(name);
    }

    @Override
    public String toString() {
        return "Test{" +
                "name='" + name + '\'' +
                '}';
    }
}

java程序的基本单位就是类,类名要符合规范,类的开始和结束是一对花括号,在开发过程中,类和方法尽量写注释。

2、类的引用

上一节的java变量中讲过,除了基本数据类型,其他的都是引用类型,那么我刚刚定义一个Person类型的变量,这个变量就称为引用。

package com.stydy.test;

public class Test {
    public static void main(String[] args) {
        // 创建了一个Person对象,通过person这个引用来访问这个对象中的方法
        Person person=new Person();
        System.out.println(person.getName());   // 菜鸡
        person.setName("大神");
        System.out.println(person.getName());   // 大神
    }
}

二、类的初始化(加载)过程

1、java程序执行流程

.java文件会被Java编译器编译为.class字节码文件,然后由JVM中的类加载器加载个各类的字节码文件,加载完毕后,交给JVM执行引擎执行,在整个程序执行过程中,JVM会用一段空间存储程序执行期间需要用到的数据和相关信息,这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们说的JVM内存,因此在Java中的内存管理就是针对这段空间进行管理(分配和回收内存空间)。流程如下

java 启动项目初始化内存 设置多少合适_父类

2、java运行时内存结构

java 启动项目初始化内存 设置多少合适_构造方法_02

栈中主要存放一些基本数据类型的变量(byte,short,int,long,float,double,boolean,char)和对象的引用。
栈的优势是,存取速度比堆快,栈数据可以共享。但缺点是,存放在栈中的数据占用多少内存空间需要在编译时确定下来,缺乏灵活性。

堆是一个运行时数据区,类的对象从堆中分配空间。这些对象通过new等指令建立,通过垃圾回收器来销毁。
堆的优势是可以动态地分配内存空间,需要多少内存空间不必事先告诉编译器,因为它是在运行时动态分配的。但缺点是,由于需要在运行时动态分配内存,所以存取速度较慢。

方法区

包括三部分:class区(存放虚拟机加载后的.class文件等类信息),静态区(存放static修饰的变量),常量池(string和包装类中的一些常量)。是各个线程共享的内存区域。

程序计算器

程序计数器是一块较小的内存空间。
当前线程所执行的字节码的行号指示器(那条字节码指令的地址)。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

介绍完各个区的功能后,接下来介绍一下变量相等的问题


三、类方法

1. 实例方法

实例方法是属于类的实例的方法。它可以访问并操作该类的实例变量和实例方法。

2. 静态方法

静态方法是属于类本身的方法,而不是类的实例。它可以直接通过类名来调用,不需要创建类的实例。静态方法只能访问和操作静态变量和调用静态方法。

3. 构造方法

构造方法是一种特殊的方法,用于创建和初始化对象。它与类的名称相同,并且没有返回类型。每次使用new关键字创建对象时,都会调用该对象的构造方法。

4. 继承方法

继承方法是指在子类中定义的方法,它继承了父类的方法。子类可以重写(覆盖)父类的方法,也可以通过super关键字调用父类的方法。

5. 重载方法

重载方法是指在同一个类中定义了多个方法名相同但参数列表不同的方法。Java根据方法名和参数类型来区分不同的重载方法。

6. 抽象方法

抽象方法是没有具体实现的方法,在抽象类或接口中声明,没有方法体的语句块。子类必须实现(重写)抽象方法,否则子类也必须声明为抽象类。

7. final方法

final方法是指不能被子类重写的方法,它在父类中被定义为final关键字。

四、方法重载

指一个类中可以定义方法名相同,但实际参数不同的一个概念,调用方法时,可以根据实际的需求调用不同参数的方法。

特点:

1、方法名相同,参数不同
2、与返回值无关
3、与访问修饰符无关

参数不同的意思是:参数的个数或者类型不同,这两个只有其中的一个不同就可以构成重载

public void test(String name,int age){}
    
    /**与上一个方法参数个数不同*/
    public void test(String name,int age,int id){}
    
    /**与第一个方法参数类型不同*/
    public void test(int age,int id){}

与返回值无关的意思是:即使返回值不同,而参数相同,也构不成方法的重载

public void test(String name,int age){}
    
    public int test(String name,int age){}

同样的,与访问修饰符无关的意思是:即使访问修饰符不同,而参数相同,也构不成方法的重载

public void  test(String name,int age){}
    
    private void test(String name,int age){}

构造方法也可以进行重载

public class Test{
    String name;
    int age;
    
    /**构造方法重载一*/
    public void Test(){
        name="aa";
        age=12;
    }
    
    /**构造方法重载二*/
    public void Test(String myName){
        name=myName;
        age=34;
    }
    
    /**构造方法重载三*/
    public void Test(String myName,int myAge){
      name=myName;
        age=myAge;
    }
}

总结
(1)对于有一个相同的需求,但具体有一些细节不一样的情况可以用到方法的重载。
当然用到方法的重载的地方还有很多,大家可以在具体开发中体会。
(2)构成重载的深层次的原因:这两个或多个方法可以被编译器识别出来,在调用的时候编译器知道要调用哪一个,不造成混淆,就构成了方法的重载。

五、类访问修饰符

访问修饰符的作用范围:
public:可以被任何类访问。
protected:可以被同一包内的类和不同包中的子类访问。
default(即没有修饰符):可以被同一包内的类访问。
private:只能被同一类内的方法访问,其他任何类都无法访问。

访问修饰符

类内部

同一包内

子类(不同包)

其他包

public





protected




default



private


访问修饰符的作用范围:

类:访问修饰符可以用于类的声明,用于控制类的可见性。
成员变量:访问修饰符可以用于类的成员变量,用于控制对成员变量的访问权限。
方法:访问修饰符可以用于类的方法,用于控制对方法的访问权限。

访问修饰符的继承性:

public:被声明为public的成员可以被任何地方的类访问,包括不同包中的子类。
protected:被声明为protected的成员可以被同一包内的类和子类访问,以及不同包中的子类。
default:被声明为default的成员可以被同一包内的类访问,但对于不同包中的类不可见。
private:被声明为private的成员只能在类内部访问,对于其他类都不可见。
访问修饰符的使用原则:

尽量限制成员的访问权限:

根据信息隐藏的原则,应该尽量将成员的访问权限限制在需要的范围内,以减少不必要的依赖和耦合。
使用最低限度的访问修饰符:应该根据需要选择最合适的访问修饰符,避免过度暴露类的内部实现细节。
考虑继承和包的结构:在设计继承关系和包结构时,需要考虑访问修饰符对继承和包的可见性的影响,以确保良好的封装性和可维护性。

总之,访问修饰符是Java中重要的概念,用于控制类成员的可见性。了解这些修饰符的含义、使用方式和作用范围可以帮助你编写更安全、可靠和易于维护的代码。

六、this和super

this

(1)this能出现在实例方法和构造方法中;
(2)this的语法是“this.”和“this()”;
(3)this不能出现在静态方法中;
(4)this大部分情况下是可以省略的;
(5)this.什么时候不能省略呢?

/** 在区分局部变量和实例变量时不能省略。例如:*/
  public void setName(String name){
	  this.name = name;
  }

(6)this()只能出现在构造方法的第一行,通过当前的构造方法去调用“本类”中的对应的构造方法,目的是:代码复用。

super

(1)super能出现在实例方法和构造方法中。

(2)super的语法是“super.”和“super()”。

(3) super不能出现在静态方法中。

(4) super大部分情况下是可以省略的。

(5)super.什么时候不能省略呢?
别急,我们想一下this指向的是什么,是当前对象自己。super和this类似,它指向了当前对象自己的父类型特征(也就是继承过来的那些东西)。

super和this区别是:this可以看做一个引用变量,保存了该对象的地址,是当前对象整体,而super代表的是父类型特征,是子类局部的一些东西,这些继承过来的东西已经在子类里面了,你可以输出整体this,但不能输出父类型特征super。因为super指向的东西不是一个整体,没法打印输出。

System.out.println(this);  //输出this.toString()的值
    System.out.println(super);  //编译报错,需要'.'

当在子类对象中,子类想访问父类的东西,可以使用“super.”的方式访问。例如:方法覆盖后,子类内部虽然重写了父类的方法,但子类也想使用一下父类的被覆盖的方法,此时可以使用“super.”的方式。当子类中出现和父类一样的属性或者方法,此时,你要想去调用父类的那个属性或者方法,此时“super.”不能省略。

this和super都只能在对象内部使用。
this代表当前对象本身,super代表当前对象的父类型特征。

“this.”是一个实例对象内部为了区分实例变量和局部变量。
而“super.”是一个实例对象为了区分是子类的成员还是父类的成员。
父类有,子类也有,子类想访问父类的,“super.”不能省略。

(6)super()只能出现在构造方法的第一行,通过当前的构造方法去调用“父类”中的对应的构造方法,目的是:创建子类对象时,先初始化父类型特征。

用通俗的话来讲,要想有儿子,得先有父亲。

我们来看下面代码:
写两个类,Animal和Cat,Cat继承Animal。

//父类,Animal类
class Animal {
	//构造函数
	public Animal() {
		System.out.println("Animal类的无参数构造函数执行");
	}
}
//子类,Cat类
class Cat extends Animal{
	//构造函数
	public Cat() {
		System.out.println("Cat类的无参数构造函数执行");
	}
}

执行下面一行代码:

public static void main(String[] args) {
	Cat cat = new Cat(); 
}

运行输出结果为:

Animal类的无参数构造函数执行
   Cat类的无参数构造函数执行

我们发现实例化一个子类的对象,也就是调用了子类的构造方法,为什么父类的无参数构造方法也执行了,并在子类构造方法执行之前就已经执行了父类的无参数构造方法,好奇怪。

刚刚在上面的super关键字的使用第6点,我已经说了,super()和this()方法一样,都只能在构造方法的第一行出现。我们猜想,难道子类的构造方法第一行有一个隐形的super()方法?答案是肯定的。

我们把子类的构造方法的第一行给它加上super():

//子类,Cat类
class Cat extends Animal{
	//构造函数
	public Cat() {
		super();
		System.out.println("Cat类的无参数构造函数执行");
	}
}

再执行下面代码:

Cat cat = new Cat();

运行输出结果为:

Animal类的无参数构造函数执行
    Cat类的无参数构造函数执行

和刚才的子类构造方法没加super()的运行结果是一样的。

所以说当子类的构造方法内第一行没有出现“super()”时,系统会默认给它加上无参数的"super()"方法

总结:

1、this和super一样,都是对象内部的引用变量,只能出现在对象内部;

2、this指向当前对象自己,super指向当前对象的父类型特征,故this的东西比super多,也就是super是this的一部分;

3、this()和super()都只能出现在构造方法的第一行,故this()和super()方法不能共存,当一个类的构造方法第一行中没有this(),也没有super(),系统默认有super()方法;

4、this()是构造方法中调用本类其他的构造方法,super()是当前对象构造方法中去调用自己父类的构造方法。