1.面向对象的基本概念—类
定义:
从一般个体中抽取出来的共性,即对某种类型个体的一般性描述(属性和行为),其实就是类。
类:同种物体在属性和行为上的集合与抽象。
类和对象的关系:
- 类描述出了,该种类型对象共有的属性和行为
- 类描述了,对象有哪些属性,具备哪些行为(包括行为的具体实现)。
- 但是各个对象的属性取什么值,只有具体的对象能确定。
Java语言中定义类,也是从这两个方面入手:
- 成员变量:就是事物的属性
- 成员方法:就是事物的行为
Java中定义类其实就是定义类的成员(成员变量和成员方法)
2.类和对象的基本语法:
类与对象的示例
- 定义学生类
分析学生类中的属性和行为,根据学生类的属性和行为来定义学生类。
//Student类
class Student {
//学号
int id;
//姓名
String name;
//性别
String gender;
//年龄
int age;
//Java课程成绩
double java;
//构造方法
public Student(int id,String name,String gender,int age,double java) {
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
this.java = java;
}
//print()方法
public void print() {
System.out.println("学号:"+id+",姓名:"+name+",性别:"+gender+",年龄:"+age+",Java课程成绩:"+java);
}
}
- 创建对象,并给具体的学生对象相应的属性赋值
类名 对象名 = new 类名();
对象名.成员变量;
对象名.成员方法
Student stu1 = new Student("张三",18,true,1);
Student stu2 = new Student("李四",25,false, 2);
stu1.print();
stu2.print();
注意: 当首次创建某个类的对象时,会触发JVM对该类的类加载过程。
一个对象的初始化过程:
当创建一个对象时,JVM会将定义该类的字节码文件加载到方法区中,同时在堆上为该对象分配内存空间,并对对象进行初始化赋值,且指向该区域的引用会被存入栈中。
- 局部变量和成员变量的比较:
①在类中定义的位置不同
a.局部变量: 定义在方法体中的变量
b.成员变量: 定义在方法体之外
②在内存中的位置不同
a.局部变量存储在方法对应的栈帧中
b.成员变量存储在堆上,在对象的内存中
③生命周期不同
a.因为局部变量是存储在栈帧中的,因此,随着栈帧的存在而存在,随着栈帧的销毁而销毁。
b.因为成员变量存储在堆上,在对象的内存空间中,因而随着对象的存在而存在,随着对象的销毁而销毁。
④初始化值不同
a.局部变量没有天生的初值,必须在使用之前用代码初始化成员变量的值。
b.成员变量存储在堆上,堆上的变量天然有默认初值。
3.构造方法:
构造方法的作用:
就是使得JVM在构造对象的时候,帮助我们进行成员变量的初始化。
构造方法的格式:
a.构造方法的方法名固定,和类名相同。
b.构造方法(没有返回值)的声明中,没有返回值一项。
//无参构造方法
public Student() {
}
注意事项:
一个类中可以,定义多个重载的构造方法。
我们需要知道,当我们没有在类中定义任何一个构造方法的时候,JVM会自动添加一个默认的构造方法(无参构造方法, 实际上什么也不会做),但是,一旦我们自己在类定义中,定义了哪怕只有一个构造方法,JVM就不会在自动帮我们添加那个无参构造方法了。
jvm创建对象的固定流程:
在堆上申请对象的内存空间 -> 给对象的成员变量赋予默认初值 ->(最后一步)调用某一个构造方法初始化成员变量的值。构造方法何时执行呢?
由JVM来真正调用执行的,在创建对象的最后一步,执行构造方法,给对象的成员变量赋予Coder传递的初值。
例:
//无参构造方法
public Student() {
}
//始化name成员变量值的1参构造方法
public Student(String name) {
this.name = name;
}
//初始化name 和 sno两个成员变量值的2参构造方法
public Student(String name,int sno) {
this.name = name;
this.sno = sno;
}
//初始化name,age,isFimale三个成员变量值的3参构造方法
public Student(String name,int age,boolean isMale) {
this.name = name;
this.age = age;
this.isMale = isMale;
}
//初始化name,age, isMale,sno这4个成员变量值的4参构造方法
public Student(String name,int age,boolean isMale,int sno) {
this.name = name;
this.age = age;
this.isMale = isMale;
this.sno = sno;
}
4.this关键字:
- 成员变量的隐藏问题:
- 方法中定义了和类中同名的成员变量。
- 在方法体中,通过同名变量的变量名来访问变量值,只能访问到方法中的那个局部同名变量的值,而访问不到同名成员变量的值。
- 在方法体中,就好像同名成员变量,被同名局部变量给隐藏起来了。
- 解决办法:
this关键字:代表当前对象的引用
当前对象的含义:
a. 在构造方法中的 this,指代的当前对象,其实就是,构造方法执行时,JVM正在创建的那个对象。
b. 在普通成员方法的方法体中,this,指代的对象是谁?
对于普通成员方法而言 对象名.方法()。所以对于普通成员方法而言,在哪个对象上掉调用方法,方法中的this就指的是哪个对象。
- this关键字的作用:
1.解决成员变量的隐藏的问题
2.访问对象的成员(访问当前对象的成员变量值,访问当前对象的成员方法)
3.访问对象的构造方法,this(实参列表) 在某个构造方法中,调用其他构造方法:
a. this调用构造方法,这个代码,只能某个构造方法的方法体中。
b. this调用构造方法, 必须处在构造方法的第一条语句的位置。
5.static关键字:
可以修饰(普通)成员变量和(普通)成员方法
- static关键字的特点:
1. 被当前类的所有对象所共享:(判定是否使用static的关键)
a. 当static修饰了成员变量,该成员变量的值,就不在存储于对象中了,而是单独存储了一份,被类的所有对象所共享。
b. 当static修饰成员方法的时候,该方法被当前类的所有对象共享。
使用方法:当前类对象.方法(和普通成员方法从共享的角度,几乎没有太大区别)
2. 可以通过类名访问(对于静态成员,官方推荐:类名.访问静态成员)
a. 可以通过类名直接访问static成员变量的值。
b. 直接通过类名直接调用static成员方法。
3. 随着类的加载而加载
a. static成员变量,随着类加载过程,其实就已经在方法区中,分配了内存,并赋予默认初值。
b. static成员方法, 一旦类加载完毕,我们就可以直接访问,static方法(通过类名.),而不必等待创建该类对象。
4. 优先于对象而存在(不依赖于对象而存在)
a. 成员变量的角度来理解,static修饰的成员变量,不再依赖于对象而存在,因为static修饰的成员变量的值,不再存储在该类的每个对象中。
作为对比,没有被static修饰的成员变量,都依赖于对象而存在,因为他们的值,都存储在对象中。
b. 成员方法角度,被static修饰的成员方法,在没有对象存在的情况下,也可以直接通过类名来调用方法。 作为对比,没有被static修饰的普通成员方法,它依赖于对象而存在,
原因是,普通成员方法中,可以访问普通成员变量的值,而普通对象的值又是依赖于对象而存在的。
注意事项:
1.所谓在静态上下文无法访问非静态的成员变量,或非静态的成员方法:
a. 无法访问当前对象的非静态成员变量或当前对象的非静态成员方法(因为当前对象可能不存在)。
b. 但是在静态方法中,可以访问我们自己new出的对象的非静态的成员变量或非静态的成员方法。
2.静态方法 or 非静态方法,方法体中都不能使用static关键字定义变量(了解即可)
静态方法的使用场景: 通常是作为工具方法来使用
之所以工具方法被定义成静态方法,是为了方便使用工具方法(静态方法),绝大多数情况下,访问的都是参数的数据。如:Array.toString()方法。
- 补充:
静态成员变量和普通成员变量区别:
①所属不同
静态变量属于类,所以也称为为类变量
成员变量属于对象,所以也称为实例变量(对象变量)
②内存中的位置不同
静态变量存储于方法区的静态区
成员变量存储于堆内存
③内存出现时间不同
静态变量随着类的加载而加载,随着类的消失而消失
成员变量随着对象的创建而存在,随着对象的消失而消失
④调用不同
静态变量可以通过类名调用,也可以通过对象调用
成员变量只能通过对象名调用
6.代码块:
- 定义:在Java中,使用{ }括起来的代码被称为代码块
根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)
① 局部代码块:(在实际开发中不会用)
1.声明方式和出现的位置: 声明方式 {}, 出现在方法体中。
2.执行时机:随着其所在的方法的执行而执行。
优点:限定变量生命周期,及早释放,提高内存利用率。 (这个优点理论上确实存在,但是这个优点,在现在jvm中其效果,微乎其微的,甚至可以忽略不计,因为我们在开发中,同时还要追求,代码可维护性)
3. 在嵌套的作用域中(代码块),不能定义同名变量。
②构造代码块:
1.声明方式和出现位置:声明方式{ },出现在类中方法体之外
2.执行时机: 每次在创建对象的时候执行。
a. 构造代码块和构造方法都是在创建对象的过程中执行的,它们的先后顺序是:构造代码块 先于 构造方法执行。
b. 成员变量的初始化语句和构造代码块,以及构造方法执行顺序的先后关系:
成员变量的初始化语句 VS 构造方法: 构造方法后执行。
成员变量的初始化语句 VS 构造代码块: 执行的先后顺序,依赖于代码书写的先后顺序。
构造代码块的使用场景:
1. 可以把多个构造方法方法中相同的,提取出来放到构造代码块中,每次调用构造都执行,并且在构造方法前执行
2. 我们也可以用,构造代码块,来初始化对象的成员变量的值
③静态代码块: static
- 声明方式和出现位置: 声明 static { }。出现的位置:类中方法体之外。
- 执行时机: 随类加载而执行,因为同一个类,在一个JVM中,最多只会被加载一次,所有静态代码块最多也只会执行一次。
使用场景: 最多只需要执行一次的代码
注意:静态代码块也是静态上下文的一种
7.package关键字:
包:类似于操作系统中的文件目录(组织文件),用来组织类
在Java源程序文件的第一行使用package声明可以使文件中定义的类成为指定包的成员
package声明的语法如下:
package 包名;
包名通常由多个名字字符串构成,中间用句点“.”分隔,每个名字表示的包称为其前面的名字表示的包的子包。
通常以组织机构的域名反转形式作为其所有包的通用前缀,比如:com.somecompany.apps
注意事项:
并非每个java文件,都必须有package关键字指定,如该源文件中的类所属的包。所以即使一个java文件中没有package关键字,该源文件中的类,也属于一个包:
默认包。
8.import关键字:
- 在类名前面加上类所属的包名,中间用句点“.”分隔,称为类的完全限定名(Full Qualified Name),简称类的限定名。
类的全限定名: 包名.类名
在Java语言中:通过类的全限定类名来唯一确定一个类的 - 当在类体中使用了与当前类不同包的类名时,编译器编译时因为无法找到该类的定义而报错,解决办法:
- 使用不同包类的完全限定名
- 使用import声明,为编译器提供该类的定义信息
import注意事项:
1.import声明一般紧跟在package声明之后,必须在类声明之前,其基本语法如下:
import 类的完全限定名;
2.Java语言核心包java.lang包中的类将被隐式导入,可以直接使用其中的类。
3.import声明提供了一种包的智能导入方式:
import <包名>.*;
包中的类将根据需要导入,避免使用多条import声明
a. 智能导包不能嵌套导包
b. 按需导包