Java—类与对象(一)
Java是完全面向对象的开发技术,每个对象中包含对用户公开的特定功能部分和隐藏的实现部分。
一、相关概念
1.1 类
类是构造对象的模板或蓝图,由类构造对象的过程称为创建类的实例。
1.1.1 类之间的关系
1.1.1.1 依赖
依赖,即“uses-a”关系,如果一个类的方法操纵另一个类的对象,就说一个类依赖于另一个类,应该尽可能减少相互依赖的类(让类之间的耦合度最小),这样一个类的改变导致其他类产生bug的概率会尽可能减少。
1.1.1.2 聚合
聚合,即“has-a”关系,即一个类的对象包含着另一个类的对象。
1.1.1.3 继承
继承,即“is-a”关系,如果类A继承类B,那么类A不仅能使用其本身的方法,还能够调用从类B继承来的方法。但类B无法使用类A的方法。
补充
- 一个文件中可以包含多个类
- 一个文件中有且只能有一个被public修饰且类名与文件名相同的类;
1.2 封装
封装(也称数据隐藏)将数据和行为组合在一个包中,隐藏其具体的实现方式,对外只提供接口、调用方式。
实现封装的关键在于不能让类的方法直接访问其他类的实例域(数据)。
需要获取或设置实例域的值时,需要提供:
- 一个私有的数据域
- 一个共有的域访问器方法
- 一个共有的域更改器方法
1.3 对象
对象是类的实例,对象中的数据称为实例域,操纵数据的过程称为方法。每个特定的类实例(对象)都有一组特定的实例域值。这些值的集合就是这个对象的状态。
同一个类的对象由于支持相同的行为而具有相似性,但每个对象都有唯一的身份标识,例如即使两个相同商品的订单也是不同的。
1.3.1 对象的三大特性
- 对象的行为—可以对对象施加的操作或方法,通过其可调用的方法定义。
- 对象的方法—向对象施加方法后,对象的响应。
- 对象标识—分辨具有相同行为与状态的不同对象。
二、预定义类
在Java中有些类没有面向对象特征,如Math类,可以通过Math.方法名(参数)对其进行调用。
2.1 构造器
想要使用对象,必须先构造对象并指定其初始状态,在Java中创建对象必须通过构造器。
- 构造器是一种特殊的方法,用来构造并初始化对象,总伴随着new一起用。
- 构造器的方法名与类名一致。
- 类中默认存在构造器,默认构造器为无参构造器。
- 可以对构造器进行重写,重写的构造器会覆盖默认构造器。
- 每个类可以有一个及以上的构造器,构造器的参数可以有0个、1个或多个参数。
- 在类中可以创建多个方法签名(方法名+参数列表)不同的构造器。
2.2 LocalDate类
public class LocalDate {public static void main(String[] args) {java.time.LocalDate a1 = java.time.LocalDate.now();java.time.LocalDate a2 = java.time.LocalDate.of(2015,06,15);System.out.println(a1); System.out.println(a2);System.out.println(a2.getYear());System.out.println(a2.getMonthValue());System.out.println(a2.getDayOfMonth());}
}
运行结果
2025-05-14
2015-06-15
2015
6
15
(1)LocalDate.now() 会构造一个新对象,返回当前日期
(2)LocalDate.of(2015,06,15) 会构造一个指定日期的新对象
(3)LocalDate对象.getYear() 获取该对象的年份
(4)LocalDate对象.getMonthValue() 获取该对象的年份
(5)LocalDate对象.getDayOfMonth() 获取该对象的日期
2.3 更改器与访问器方法
更改器方法:调用方法后,对象状态会改变。
访问器方法:只访问对象而不修改对象的方法。
示例:日历
package com.qcby.第四章;import java.time.DayOfWeek;
import java.time.LocalDate;public class 日历 {public static void main(String[] args) {LocalDate date = LocalDate.now();int month = date.getMonthValue();int today = date.getDayOfMonth();date = date.minusDays(today-1);DayOfWeek weekday = date.getDayOfWeek();int value = weekday.getValue();System.out.println("Mon Tue Wed Thu Fri Sat Sun");for(int i = 1;i<value;i++) {System.out.print(" ");while(date.getMonthValue() == month) {System.out.printf("%3d",date.getDayOfMonth());if(date.getDayOfMonth() == today)System.out.print("*");elseSystem.out.print(" ");date = date.plusDays(1);if(date.getDayOfWeek().getValue()==1) System.out.println();}if(date.getDayOfWeek().getValue()!=1) System.out.println();}}
}
运行结果
Mon Tue Wed Thu Fri Sat Sun1 2 3 4 5 6 7 8 9 10 11 12 13 14* 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
三、自定义类
3.1 显式参数与隐式参数
隐式参数是指在方法中使用但没有在方法参数中声明的变量。通常,这些变量是在类中声明的实例域。隐式参数的一个典型例子是使用this关键字引用当前对象的实例域。
显式参数会明显的列在方法声明中,如下面代码中的int num。
public class Test{public int a = 10; //隐式参数//this 关键字指向当前对象实例,但静态方法不依赖于实例,所以在静态方法中不能使用this关键字public void b(int num) { //num 显式参数 void声明没有返回数据,若有返回需要写明类型this.a+=num;System.out.println(a);}public static void main(String[] args) {new LocalDate().b(5);}
}
运行结果
15
3.2 this关键字
this关键字是一个指向当前对象实例的引用。它的核心作用是帮助在对象的方法或构造器中明确访问当前实例的成员。
3.2.1 this关键字用途
3.2.1.1 解决成员变量与局部变量同名冲突
public class test {private String name; // 成员变量public void setName(String name) { // 参数名与成员变量同名this.name = name; // 用 this 明确指向成员变量System.out.println(name);}public static void main(String[] args) {new test().setName("nameA");}
}
运行结果
nameA
- 当方法参数与成员变量同名时,this用于区分成员变量和局部变量。
3.2.1.2 在构造器中调用其他构造器
public class test {private String name; public static void main(String[] args) {System.out.println(new test());}public test(String name) {this.name = name;}public test() {this("nameA"); //必须是构造器的第一条语句}@Overridepublic String toString() {return "test [name=" + name + "]";}}
运行结果
test [name=nameA]
规则:不使用方法名,直接用this(...)调用,且必须是构造器的第一条语句。
用途:减少重复代码,实现构造器的链式调用。
3.2.1.3 返回当前对象实例
public class test {private int count; public test Count() {count++;++count;return this; //返回当前对象,实现链式调用}public static void main(String[] args) {System.out.println(new test().Count());}@Overridepublic String toString() {return "test [count=" + count + "]";}
}
运行结果
test [count=2]
3.2.1.4 内部类访问外部类实例
public class test {private int count = 5; class inner{public void a() {System.out.println(test.this.count); //访问外部类成员}}@Overridepublic String toString() {return "test [count=" + count + "]";}
}
测试
public class test1 {public static void main(String[] args) {test a = new test();System.out.println(a);}
}
运行结果
test [count=5]
注意:内部类中直接使用this指向的是内部类实例,需通过外部类名
.
this访问外部类成员。
3.2.2 this关键词的限制
(1)不能在静态方法中使用
静态方法属于类而非实例,this无指向目标。
(2)不能在静态代码块中使用
静态代码块在类加载时执行,此时没有对象实例。
(3)在构造器中调用必须位于第一行
通过this()调用其他构造器时,必须作为构造器的第一条语句
3.2.3 与super()区别
3.2.3.1 super()的用途
3.2.3.1.1 显式调用父类指定构造器
父类
public class test {public int count; public test(int count) {super();this.count = count;}
}
子类
public class test1 extends test{String a;public test1(int count, String a) {super(count);this.a = a;}public static void main(String[] args) {System.out.println(new test1(5,"A"));}@Overridepublic String toString() {return "test1 [a=" + a + " count="+count+"]";}}
运行结果
test1 [a=A count=5]
若父类没有无参构造器,子类构造器必须显式调用super(参数)。
3.2.3.1.2 访问被覆盖的父类成员
父类
public class test {void print() {System.out.println("Parent");}
}
子类
public class test1 extends test{@Overridevoid print() {super.print(); // 调用父类被覆盖的方法System.out.println("Child");}public static void main(String[] args) {new test1().print();}
}
运行结果
Parent
Child
3.2.3.1.3 解决子类与父类字段名冲突
父类
public class test {int count = 100;
}
子类
public class test1 extends test{int count = 200;void show() {System.out.println(super.count); // 访问父类的 count (100)System.out.println(this.count); // 访问本类的 count (200)} public static void main(String[] args) {new test1().show();}
}
运行结果
100
200
3.2.3.2 super()限制
3.2.3.2.1 super()和this()不能共存
如图,若super()和this()同时存在,在下面的那个会报错。
3.2.3.2.2 默认的super()陷阱
class Parent {Parent(int num) { } //父类没有无参构造器
}class Child extends Parent {Child() { //隐式super()会尝试调用Parent(),但父类没有无参构造器}
}
修改
Child() {super(100); // 强制指定父类构造器
}