面向对象是 Java的主要特性之一,是一种重要的编程思想。我们都知道面向对象有三大特性(封装、继承和多态),但学习它们之前,首先要做的基本功就是了解什么是类、对象还有方法,以及它们之间的相关操作和分类,内容大概如下。
一、对象/类
1、概念
1.1 对象(object)
Java中一起皆对象。那么什么是对象?简单理解,对象是一个实例(instance),有状态(属性)和行为(方法)。eg:
学生是一个对象,有姓名、性别、年龄、学号、所属班级、成绩等属性。学生可以有吃饭、睡觉、学习等行为,这些即方法。
员工是一个对象,有姓名、性别、年龄、工号、所属部门、绩效等属性。员工可以有吃饭、睡觉、摸鱼等行为,这些即方法。
不只是人,动物(🐖,🐱,🐕...),植物(花、草、树...)都是对象。简言之,世间一切皆对象。
1.2 类(class)
说完对象,再来说类。说到类,很多人可能会想到"物以类聚,人以群分"。类是一个模板,描述了一类对象的行为和状态。很好理解,eg:
人是一个类,具体每一个人如小明、老王就是一个对象。(这里人就是一个抽象的概念,因为可以再细分如男人/女人)
狗是一个类,具体如沙皮狗、哈巴狗就是一个对象,虽然属于不同的品种,但他们都有狗的特征和行为,都是狗。(这里狗就是一个抽象的概念,因为可以再细分)
可以看到,类可以划的比较宽泛,也可以划的细一点,具体还要看实际的业务需求。简言之,对象是类的实例,类是对象的抽象。
2、深入
2.1 类的定义
1)语法
[修饰符] class 类名 {
0-N 成员变量(字段) // 描述类具有的特性,对象的状态
0-N 构造器
0-N 方法 // 描述类具有的功能,对象的行为
}
pass:类可看做是一种自定义的数据类型,可以使用类来定义变量。
关于修饰符之前介绍过,定义类 public、protected、abstract和 static用的较多,各有不同的含义和功能。
成员变量(非静态变量 / 实例变量 / 全局变量):实例变量声明在一个类中,但在方法、构造方法和语句块之外(类中方法外)。
至于构造器和方法,接下来会讲到。
2.2 对象创建、访问/修改属性
1)创建对象
创建对象即实例化一个对象。一般通过new关键字来创建对象。还可以通过反射和序列化创建,当然,这都是后话了。
类名 对象名 = new 类名();
2)访问/修改
类/对象的访问成员变量或方法如下
通过 对象.属性 直接赋值的方式修改属性
类.类方法
类.类变量
对象名.成员方法
对象名.成员变量
3)eg
public class Person { // 定义一个 Person 类,有姓名和年龄两个属性
String name; // 姓名
int age; // 年龄
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
class PersonTest{ // 测试类
public static void main(String[] args) {
Person p=new Person(); // 创建一个对象 p
p.name="zhangsan"; // 赋值
p.age=20;
System.out.println(p.name+" "+p.age);
p.name="lishi"; // 通过对象.属性直接赋值的方式修改属性
System.out.println(p.name+" "+p.age);
p.eat(); // 调用方法
p.sleep();
}
}
测试结果
zhangsan 20
lishi 20
吃饭
睡觉
pass:Java中的测试类是用来是用来对已经写好的功能组件进行测试。上面这种通过新建一个带有main方法的类,利用该类来调用需要测试的类,并把需要测试的数据传入进去的测试方法在前期学习是非常常用的,后期开发则采用更加高效便捷的JUnit单元测试。
2.3 常用关键字
以下部分可能刚开始不是那么好理解,慢慢就熟悉了。
1)静态 static
static 修饰的成员称为类成员(类变量、类方法、静态初始化块、静态内部类),其随着所在类的加载而加载,先于该类对象存在(当字节码被加载进 JVM 时类成员就存在了,而对象是后面 new 出来的),被该类所有对象所共享,可以直接用类名调用。
注:被 static修饰的成员方法和成员变量称为类成员,其中,被 static修饰的成员变量称为类变量,被 static修饰的成员方法称为类方法。
2)未被 static修饰
相反,未被 static修饰的成员称为与实例成员(实例变量、实例方法、普通初始化块、实例内部类)
注:未被static修饰的成员方法和成员变量称为实例成员,其中,未被static修饰的成员变量叫做实例变量,未被static修饰的成员方法叫做实例方法。
3)其他注意
局部变量(类的方法中、代码块中的变量)不属于任何类或实例,不能使用 static 修饰;不能修饰构造器。
public只能由对象访问(对象.属性|方法),而 static 静态方法可以在不创建类的对象的情况下访问该方法。
static 修饰的成员(属于类)及构造器不能直接访问没有 static 修饰的成员(属于对象)
由于static 强调的是类,this 和 super 强调的是对象,故 static 修饰的方法中不能使用 this 或 super 引用
4)代码示例(重点)
搞清楚如下代码示例(结合注释)就基本上能理解上面的表达意思。至于第 3)点涉及的构造器、this会在接下来讲,这里我们先记住。
public class Person { // 定义一个 Person 类
String name;
int age; // 实例变量(成员变量)
static String gender; // 类变量
public static void eat(){ // 类方法
System.out.println("吃饭");
}
public void sleep(){ // 实例方法(成员方法)
// static int i; // 错误,局部变量不能使用 static 修饰
System.out.println("睡觉");
}
}
class PersonTest{ // 测试类
public static void main(String[] args) {
// static修饰的成员可以直接用类名调用。
Person.gender="男";
Person.eat(); // static 静态方法可以在不创建类的对象情况下访问该方法
Person p=new Person(); // 创建一个对象 p
p.name="zhangsan"; // public只能由对象访问(对象.属性|方法)
p.age=20;
p.sleep();
// static修饰的成员被该类所有对象所共享,故也能由对象访问(对象.属性|方法)
p.gender="女"; // 通过对象.属性直接赋值的方式修改属性
p.eat();
System.out.println(p.name+" "+Person.gender+" "+p.age);
}
}
测试结果(建议自己把代码跑一遍,加深理解)
吃饭
睡觉
吃饭
zhangsan 女 20
5)本类 this
this 是 Java 常用的关键字,可用于任何实例方法内指向当前对象。在方法内部,可以使用隐含的变量 this,它始终指向当前实例。并通过 this.field 就可以访问当前实例的字段(即属性)。若无命名冲突,可以省略 this。eg
public class Person { // 定义一个 Person 类
private String name;
public String getName() { // 实例方法
return name; // 相当于this.name
}
}
若有局部变量和字段重名,则局部变量优先级更高,就必须加上 this。eg
public class Person {
private String name;
public void setName(String name) {
this.name = name; // 前面的this不可少,少了就变成局部变量name
}
}
2.4 设置和获取对象数据
1)设置:
- setter()(属性注入)
- 构造注入:直接调用带参数的构造器,创建对象的同时完成对象字段的初始化(推荐)。
2)获取:
- getter()方法
3)eg:看懂以下示例即可理解该部分内容
public class Person {
private String name;
private int age;
public Person() { //无参构造
}
public Person(String name, int age) { //带参构造
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class TestPreson{ // 测试类
public static void main(String[] args) {
Person p1=new Person();
// 1、setter(属性注入)初始化
p1.setName("zhangsan");
p1.setAge(13);
// 2、构造注入,调用构造方法的同时完成初始化(推荐)
Person p2=new Person("lishi",14);
System.out.println(p1.getName()+" "+p1.getAge()); // zhangsan 13
System.out.println(p2.getName()+" "+p2.getAge()); // lishi 14
}
}
二、方法
方法又称函数,是一个代码块,用于执行某些操作,只在运行时调用。
1、按参数和返回值
其实可以不用分这么细,常用的就那几种,其他了解即可。
1.1 无参无返回值
一般方法内为输出打印某个内容。用直接调用即可。
void:无返回值,有返回值(return)不用 void修饰符。
public class Person {
public static void main(String[] args) {
eat(); // 直接调用
}
public static void eat(){ // 无参无返回值
System.out.println("吃饭");
}
}
1.2 带参无返回值
同上,直接调用。
public class Person {
public static void main(String[] args) {
getSum(10,20); //直接调用(记得传参)。
}
public static void getSum(int a, int b){ //带参无返回值
System.out.println(a+b);
}
}
1.3 无参有返回值
public class Person {
public static void main(String[] args) {
String massage=getMassage(); //赋值调用
System.out.println(massage);
System.out.println(getMassage()); //输出调用
}
public static String getMassage(){ //无参有返回值
return "Hello!";
}
}
1.4 带参有返回值(常用)
带参有返回值方法的定义和调用。一般情况最常用赋值调用。
public class Person {
public static void main(String[] args) {
getSum(10,20,30); // 1.直接调用(运行不输出结果)
int sum=getSum(10,20,30); // 2.赋值调用
System.out.println(sum);
System.out.println(getSum(10,20,30)); // 3.输出调用
}
public static int getSum(int num1,int num2,int num3){ // 带参有返回值
return num1+num2+num3;
}
}
2、构造方法
构造方法是一种特殊的方法,用于创建对象时执行必要的初始化操作。
注:构造方法的名称必须与类名相同,无返回值,且必须通过 new操作符调用构造方法。
2.1 创建实例,初始化实例字段
在创建实例(对象)时,经常需要同时初始化这个实例的字段(属性)。
法1:通过setter()方法,每次需要调用该方法,比较麻烦。
Person p = new Person();
p.setName("老王");
p.setAge(11);
System.out.println(p.getName()+" "+p.getAge());
法2:通过构造方法,在创建实例时就把所有属性都初始化。(推荐)
Person p=new Person("老王",11);
System.out.println(p.getName()+" "+p.getAge());
2.2 默认构造方法(重点)
若未定义构造方法,编译器会自动生成一个默认构造方法(无参构造)。定义了构造方法,编译器不再自动创建默认构造方法。
一般而言,最好就是把两个构造方法都定义出来,如下。
public class Person {
private String name;
private int age;
public Person() { //默认构造(无参构造)
}
public Person(String name, int age) { //带参构造
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class TestPreson{ // 测试类
public static void main(String[] args) {
Person p1=new Person("zhangsan",20); //既可调用带参构造方法
Person p2=new Person(); //也可调用无参构造方法
System.out.println(p1.getName()+" "+p1.getAge()); // zhangsan 20
System.out.println(p2.getName()+" "+p2.getAge()); // null 0
}
}
pass:通过 new 方式调用构造方法。
2.3 创建实例初始化顺序
先初始化字段(属性),再初始化构造方法。
public class Person {
private String name = "zhangsan"; // 先字段初始化
private int age = 20;
public Person(String name, int age) {
this.name = name; //再构造方法初始化
this.age = age;
}
}
2.4 多构造方法
1)多个构造方法的重载,编译器会根据参数自动判断。如下
public class Person {
private String name;
private int age;
// 创建对象,初始化 name 和 age
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this.name = name;
this.age = 12;
}
// 默认构造器(编译器自动产生)
public Person() {
}
}
2)实现构造方法间的相互调用(调用语法:this(, , ...)),以提高代码复用性。如下
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this(name, 18); // 调用另一个构造方法Person(String, int)
}
public Person() { //无参构造
this("lishi"); // 调用另一个构造方法Person(String)
}
// 提供 getter()和setter()方法,这里只用到getter()
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class TestPreson{ // 测试类
public static void main(String[] args) {
Person p=new Person("zhangsan",19); // 调用两个参数的构造方法
//调用无参构造,层层传递,Person()-->Person(String)-->Person(String, int)
Person p1=new Person();
System.out.println(p.getName()+" "+p.getAge());
System.out.println(p1.getName()+" "+p1.getAge());
}
}
测试结果
zhangsan 19
lishi 18
分析:(理解了上述代码的可忽略)
第一个输出大家应该都没问题,因为 p调用两个参数的构造方法,并通过构造方法,在创建实例时就把所有属性都初始化,所以输出 zhangsan 19。
来看第二个输出,p1调用的是无参构造方法,但由于Person()无参构造方法又通过 this("lishi")调用了 Person(String name)构造方法,且初始化 name为 lishi,所以 p1.getName()为 lishi,而 Person(String name)构造方法又通过 this(name, 18)调用了 Person(String name, int age)构造方法,且初始化 age为18,所以 p1.getAge()为 18。
2.5 总结
1)实例在创建时通过new操作符会调用其对应的构造方法(用于初始化实例)。
2)没有定义构造方法时,编译器会自动创建一个默认构造方法(无参构造)。
3)显式定义了构造器(构造方法)之后,编译器就不会自动生成默认构造器了,但可以自己手动加上。
4)可定义多个构造方法,编译器会根据参数自动判断。
5)可在一个构造方法内部调用另一个构造方法,便于代码复用。
3、方法重载(重点)
之后在讲到继承时还会有一个重写,要与重载区别开,先打预防针。
3.1 介绍
方法重载(Overload):方法名相同,参数不同。
使用重载的目的:功能类似的方法使用同一名字,更容易记住,调用起来更简单(在参数上做修改即可)。
参数不同(体现在)
- 参数的个数不同
- 参数的类型不同
- 参数的个数和类型都不同
3.2 举例
1)如 String类提供了多个重载方法 indexOf(),用于查找子串
int indexOf(int ch):根据字符的Unicode码查找;
int indexOf(String str):根据字符串查找;
int indexOf(int ch, int fromIndex):根据字符查找,但指定起始位置;
int indexOf(String str, int fromIndex)根据字符串查找,但指定起始位置。
2)再举一个简单例子,如下,实现 hello方法的重载。
运行结果public class Person { public static void main(String[] args) { String h = hello(); String h1 = hello("zhangsan"); String h2 = hello("zhangsan", 16); System.out.println(h); System.out.println(h1); System.out.println(h2); } public static String hello() { return "hello,world"; } public static String hello(String name) { return "Hello," + name; } public static String hello(String name, int age) { return "Hello," + name+" "+age; } }
hello,world Hello, zhangsan Hello, zhangsan 16
pass:这次的内容真的有点多,希望能帮助到大家,能看到这里你就是最棒的!