我们都知道,Java是一门面向对象的编程语言,而面向对象的编程范式最大的特点就是组织程序都是对象与对象之间的交互,涉及到的一系列属性和方法都属于某个具体的对象。
什么是类
类是一种比较抽象的概念,描述同一类对象所具备的共同属性和行为,在类中只规定了一类对象所共同具备的属性,而属性值不定。
类的语法规则
在java中定义一个类使用class关键字,程序中所有的属性和行为都必须放在某个类中定义。类名采用大驼峰命名法。
//定义类的语法
class 类名称{
一系列的属性;//成员变量、实例变量、字段(属性)
一系列的方法;//成员方法、实例方法
}
//定义一个Dog类
class Dog{
//这些属性称为类的成员变量,当狗这个类产生对象时,每个对象的这些属性值可能不同
public String name; // 名字
public int age; // 年龄
public String color; // 颜色
//这些方法称为成员方法,当有了具体对象时才能调用这些方法
public void bark() {
System.out.println(name + "正在汪汪叫");
}
public void wag() {
System.out.println(name + "正在摇尾巴~");
}
}
其中,在一个.java源文件中,public class称为主类,一个源文件中只能有一个主类并且必须和源文件的名称保持一致。
什么是对象
对象是类的一个具体表现,当产生对象之后,类中的属性才了具体的数值,对象才是实实在在的实体。
产生对象的语法
用类产生对象的过程称为类的实例化,使用关键字new产生对象。
//产生对象的语法:
类名称 对象引用名称 = new 类名称()
// 以上述定义好的Dog类为例创建Dog类的对象
Dog six = new Dog();
// 有对象之后成员属性和成员方法就可以进行调用了~~
// 成员属性和方法必须通过对象来调用("." 引用操作符)
// 通过引用操作成员变量
six.name = "六六";
six.color = "咖啡色";
// 通过引用操作成员方法
six.bark();
six.wag();
// 再创建一个Dog类对象
Dog mu = new Dog();
mu.name = "牧牧";
mu.color = "白色";
mu.bark();
mu.wag();
注意,同一个类可以产生多个实例对象,new Dog()称为Dog类的对象,存储在堆区。six称为引用名称,保存了新产生的对象堆内存地址,而自己本身存储在栈区。
同一个类中不同对象的比较
若要判断同一个类的不同对象之间的内部属性值是否一致,调用equals方法,是向下转型的一种应用,后续会学习记录。
构造方法
构造方法是类中特殊的成员方法,构造方法的名称必须和类名称相同,在创建对象时,由编译器自动调用,在对象的整个生命周期中只调用一次。
在类中定义构造方法的规则:
1、方法名称必须和类名称相同。
2、构造方法没有返回值的类型声明,不是void。
class Dog {
//正确写法
public Dog() {
System.out.println("Dog类定义时提供的构造方法");
}
//错误写法
//不是构造方法,是一个命名不规范的普通成员方法
//产生Dog类对象是不会自动执行这个方法
public void Dog() {
System.out.println("Dog类定义时提供的构造方法");
}
、、、、、、
}
3、当类中没有提供构造方法定义时,Java编译器在编译之后会默认生成无参构造(不带参数的构造方法)。一旦类中提供了构造方法,编译器就不会在生成默认的无参构造。
//定义一个Dog类,此时没提供明确的构造方法
class Dog{
public String name; // 名字
public int age; // 年龄
public String color; // 颜色
public void bark() {
System.out.println(name + "正在汪汪叫");
}
public void wag() {
System.out.println(name + "正在摇尾巴~");
}
}
没有提供构造方法时,javac之后会默认自动生成无参构造。
而提供了构造方法时,编译之后默认的无参构造不再产生。
//定义一个Dog类,提供明确的构造方法
class Dog{
public String name; // 名字
public int age; // 年龄
public String color; // 颜色
public Dog() {
System.out.println("Dog类定义时提供的构造方法");
}
public void bark() {
System.out.println(name + "正在汪汪叫");
}
public void wag() {
System.out.println(name + "正在摇尾巴~");
}
}
构造方法的作用:
在产生对象时,为对象的所有成员属性做初始化操作。
首先先了解清楚new Dog()
操作背后JVM做了什么:
1、检测Dog类是否加载到JVM内存中,若没加载先把Dog类加载到内存中。
2、只要有new就要在堆上产生新的空间,为Dog对象在堆上分配空间。
3、初始化对象分配的空间(此时要用到构造方法进行属性的初始化)
Java中所有数据类型都有默认值,这个默认值必须结合类来观察,只有在类中定义的成员变量才有默认值,这个默认值就是在产生对象时,由构造方法进行的属性初始化。
成员变量在堆中存储,具体存储在每个对象的内部空间中。
由于构造方法在产生对象时,为所有成员变量赋值,因此可以在定义类时,给某些属性“就地初始化”特殊的默认值(不是数据类型的默认值)。
构造方法的重载
一般指参数列表(参数个数)不同的构造方法,通过构造方法的参数来为对象的属性初始化操作。在定义构造方法的重载时,按照构造方法的参数个数来定义。
若此时该类中只有一个带有两个参数的有参构造,则编译器不会生成默认的无参构造!! 此时调用无参构造方法会报错。
this关键字
1、this修饰成员变量,表示直接去类中寻找同名变量
先看一种情况:
在这种情况下,程序的就近匹配原则,编译器会从代码位置开始搜索距离代码位置最近的同名变量在哪。上述情况相当于形参自己等于自己,根本没用到类中的成员变量。此时加上this关键字即可。
2、this修饰成员方法,表示直接从当前类中寻找同名方法。
语法:this.方法名称(参数)
在同一个类中,没有继承关系,this也可以省略。
3、this修饰构造方法,语法:this(参数)
,表示调用构造方法
三大要求:
a. this调用构造方法只能在构造方法中使用,不能在成员方法中使用。
b、this调用其他的构造方法必须写在当前构造方法的首行。
c、this的调用必须是线性的,不能成环。成环调用程序永远无法终止!
static关键字
1、static修饰属性,则该属性为静态变量,直接通过类名称就可以调用,存储在JVM的方法区,这个类的所有对象共享此变量(只要由一个对象改了该属性的值,对其他对象都是可见的)。
关于静态变量的说明:
a、静态变量只存在于类中,方法中千万不能定义静态变量!!!
b、静态属性直接通过类名来访问,没有任何该类的对象这个属性也存在。
j1虽然为null,但是j1仍然是一个Japanese类的引用,相当于直接使用了类名称Japanese访问了country属性。若此时country不是静态变量,则会报空指针异常(NPE)。
通过引用访问属性时到底会不会报错要看访问的是成员变量还是静态变量,成员变量必须通过对象来访问,静态变量通过类来访问。
c、静态变量存储在JVM的方法区,所有该类对象共享。
当Japanese类加载到JVM内存之中,静态变量就会在方法区初始化并存储,之后所有的Japanese对象的country属性都是共享方法区中的country属性。
2、static修饰方法,称为类方法或者静态方法,和具体对象无关,通过类名称直接访问,例如Math.max()
等。
关于static的几点说明:
1、static修饰的属性或方法都称为类的静态域,静态域没有该类的对象就能访问,通过类名称来访问。而在类中不加static关键字定义的属性或方法称为成员域,必须有对象才能访问。
2、static不能修饰一个普通类,原因在于类定义出来是为了产生对象,static若修饰类没对象也可以,自相矛盾。
但是static可以修饰一个内部类。
3、在同类中,在静态方法中不能访问成员变量,但可以访问静态变量,直接调用属性名;而在成员方法中可以访问静态变量,直接调用属性名,也可以访问静态方法。
4、this修饰的是当前对象的属性或方法,是成员域的一部分,故静态方法不能出现this关键字。
代码块
使用"{ }"括起来的一段代码称为代码块,按代码块出现的位置以及关键字可分为以下四种:
1、普通代码块:定义在方法中,直接用{}括起来的代码块称为普通代码块。普通代码块中定义的局部变量作用域仅限于当前代码块的内部可见。
2、构造块:定义在类的内部,不加任何修饰符的代码块称为构造块。
构造块优先于构造方法执行,有几个对象产生,就执行几次构造块的代码。构造块一般用在构造方法之前为类中的成员变量进行初始化操作。
一个类可以存在多个构造代码块,多个代码块在编译之后都会合并在构造方法的最前面。
3、静态代码块:定义在类中,使用关键字static修饰的代码块称为静态代码块。在该类被JVM加载时,无论有多少对象产生,静态块只加载一次,优先于构造块和构造方法。一般用于检查某些类是否被JVM加载或为当前类的静态变量进行初始化操作。
a、在普通类中的静态块。
b、若当前类中存在主方法,静态块还会优先于主方法执行(主方法存在于类中,要启动主方法,需要将主类加载到内存中,此时静态块一加载就执行)。
c、类比构造块,静态块也可以有多个,多个静态块按照书写顺序,编译完之后会合并为一个大的静态块。
4、同步代码块(后续学习多线程时再记录~)。
封装
封装的作用:易用性和保护性
private关键字修饰属性和方法只在当前类的内部可见,出了这个类,无法调用,对外部隐藏。
1、保护性。使用private封装。例如银行卡这个类,卡号、密码和余额这三个属性都应该是需要被隐藏的,否则就可以随意更改这三个属性值,无法管控。
当属性被private封装(隐藏)之后,在类的外部要想使用这些属性,必须通过类对外提供的方法来使用。
getter
: 在类的外部获取某些private属性值;
setter
: 在类的外部设置某些private属性值。
2、易用性:汽车的一键启动
注意:private不能修饰类。类定义出来是为了创建对象,private修饰的内容只在类中可见,如果private可以修饰类,那么类定义之后就隐藏了,矛盾。
包
Java中的访问修饰权限,由大到小:
private
(当前类的内部可见) < default
(包访问权限,千万记住不要写这个关键字,权限不写默认就是包权限)<protected
(继承中使用)< public
(公开的,当前项目的内部可见)
1、包的定义:在源文件中的首行使用package来定义当前类位于哪个包下。
2、包的导入:Java中使用import关键字来导入某个包中的类,所有的导入都导入的是具体的类,不可能将整个文件夹导入。
a、通过全名称导入
b、若当前源文件中还使用了这个包下的其他类,可以使用通配符导入。这种导入不是将整个包下的所有类全部一次性导入当前源文件,还是按需导入,这种写法属于编译器的一个"语法糖",只存在于源代码中,编译之后,javac编译器会按照当前类中实际到的类还原为具体类的导入。
但当多个不同的包下都使用通配符导入时,若多个包出现了相同的类,就会发生冲突!
c、静态导入:将某个包下的某个类中的所有静态域导入到当前类中。import static 包名.类名. *
常规使用其他类的静态域,都需要使用类名称.方法名称/属性名称
静态导入类中的静态域时,直接调用这些方法/属性。
3、包的访问权限:若某些属性/方法/类使用包访问权限定义时,表示这些属性/方法/类只在当前包(同级目录)中可见,子目录中不可见。
注意:关于当前包的子包可见性:
包访问权限对于子目录仍然隐藏。
4、JDK中常用的包
java.lang
:系统常用的基础类都在这个包下(String、Math、Object),此包在JDK1.1之后自动导入。
java.util
:是java提供的工具类,都在这个包下,整个java的集合框架都在此包中(ArrayList、HashMap)。
java.sql
:数据库开发包,JDBC的所有类都在此包中。
java.net
:网络编程开发包,Socket编程相关类在此包中。
java.io
:I/O开发包。