面向对象是 Java的主要特性之一,是一种重要的编程思想。我们都知道面向对象有三大特性(封装、继承和多态),但学习它们之前,首先要做的基本功就是了解什么是类、对象还有方法,以及它们之间的相关操作和分类,内容大概如下。
Java中一起皆对象。那么什么是对象?简单理解,对象是一个实例(instance),有状态(属性)和行为(方法)。eg:
学生是一个对象,有姓名、性别、年龄、学号、所属班级、成绩等属性。学生可以有吃饭、睡觉、学习等行为,这些即方法。
员工是一个对象,有姓名、性别、年龄、工号、所属部门、绩效等属性。员工可以有吃饭、睡觉、摸鱼等行为,这些即方法。
不只是人,动物(,,…),植物(花、草、树…)都是对象。简言之,世间一切皆对象。
说完对象,再来说类。说到类,很多人可能会想到"物以类聚,人以群分"。类是一个模板,描述了一类对象的行为和状态。很好理解,eg:
人是一个类,具体每一个人如小明、老王就是一个对象。(这里人就是一个抽象的概念,因为可以再细分如男人/女人)
狗是一个类,具体如沙皮狗、哈巴狗就是一个对象,虽然属于不同的品种,但他们都有狗的特征和行为,都是狗。(这里狗就是一个抽象的概念,因为可以再细分)
可以看到,类可以划的比较宽泛,也可以划的细一点,具体还要看实际的业务需求。简言之,对象是类的实例,类是对象的抽象。
1)语法
[修饰符] class 类名 {
0-N 成员变量(字段) // 描述类具有的特性,对象的状态
0-N 构造器
0-N 方法 // 描述类具有的功能,对象的行为
}
pass:类可看做是一种自定义的数据类型,可以使用类来定义变量。
关于修饰符之前介绍过,定义类 public、protected、abstract和 static用的较多,各有不同的含义和功能。
成员变量(非静态变量 / 实例变量 / 全局变量)
在类中方法外:实例变量声明在一个类中,但在方法、构造方法和语句块之外。
至于构造器和方法,接下来会讲到。
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单元测试。
以下部分可能刚开始不是那么好理解,慢慢就熟悉了。
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
}
}
1)设置:
2)获取:
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
}
}
方法又称函数,是一个代码块,用于执行某些操作,只在运行时调用。
其实可以不用分这么细,常用的就那几种,其他了解即可。
一般方法内为输出打印某个内容。用直接调用即可。
void:无返回值,有返回值(return)不用 void修饰符。
public class Person {
public static void main(String[] args) {
eat(); // 直接调用
}
public static void eat(){ // 无参无返回值
System.out.println("吃饭");
}
}
同上,直接调用。
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);
}
}
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!";
}
}
带参有返回值方法的定义和调用。一般情况最常用赋值调用。
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;
}
}
构造方法是一种特殊的方法,用于创建对象时执行必要的初始化操作。
注:构造方法的名称必须与类名相同,无返回值,且必须通过 new操作符调用构造方法。
在创建实例(对象)时,经常需要同时初始化这个实例的字段(属性)。
法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());
若未定义构造方法,编译器会自动生成一个默认构造方法(无参构造)。定义了构造方法,编译器不再自动创建默认构造方法。
一般而言,最好就是把两个构造方法都定义出来,如下。
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 方式调用构造方法。
先初始化字段(属性),再初始化构造方法。
public class Person {
private String name = "zhangsan"; // 先字段初始化
private int age = 20;
public Person(String name, int age) {
this.name = name; //再构造方法初始化
this.age = age;
}
}
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。
1)实例在创建时通过new操作符会调用其对应的构造方法(用于初始化实例)。
2)没有定义构造方法时,编译器会自动创建一个默认构造方法(无参构造)。
3)显式定义了构造器(构造方法)之后,编译器就不会自动生成默认构造器了,但可以自己手动加上。
4)可定义多个构造方法,编译器会根据参数自动判断。
5)可在一个构造方法内部调用另一个构造方法,便于代码复用。
之后在讲到继承时还会有一个重写,要与重载区别开,先打预防针。
方法重载(Overload):方法名相同,参数不同。
使用重载的目的:功能类似的方法使用同一名字,更容易记住,调用起来更简单(在参数上做修改即可)。
参数不同(体现在)
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:这次的内容真的有点多,希望能帮助到大家,能看到这里你就是最棒的!