第七课:类与对象
一、类
-
类是一组相关属性和行为的集合,是一个抽象的概念
-
类体中一般定义三类要素:成员变量和常量、构造方法和方法。
- 成员变量和常量用来刻画对象的状态
- 方法用来描述对象的行为
- 构造方法一般用来初始化成员变量
1、类的声明
类声明定义了类的名字及其他属性。类声明的一般格式如下:
[类修饰符] class 类名[extends 父类名称][implements 接口名称列表]
{
成员变量;
成员方法;
...
}
解释:
- class关键字和类名是必需的,其他都视情况保留
- [ ]表示可选项
- 类名是要声明的类的名字,它必须是一个合法的Java标识符,习惯上首字母要大写。
(1)[ 类修饰符 ]
类修饰符有public、abstract和final。如果没有声明这些类修饰符,Java编译器默认该类为friendly类,对于这些类,只有同一包中的类可以访问
1) public(公共的)
- 带有public修饰符的类称为公共类
- 公共类可以被任何包中的类访问。不过,要在一个类中使用其他包中的类,必须在程序中增加import语句
2) abstract(抽象的)
-
带有abstract修饰符的类称为抽象类,相当于类的抽象
-
一个抽象类可以包含抽象方法,而抽象方法是没有方法体的方法。
所以:
- 抽象类不具备具体功能,只用于衍生出子类
- 抽象类不能被实例化。
3) final(最终的)
- 带有final修饰符的类称为最终类
- 不能通过扩展最终类来创建新类。也就是说,它不能被继承,或者说它不能派生子类
(2) [ extends 父类名称 ]——extends关键字
-
extends关键字用来告诉编译器创建的类是从父类继承来的子类,用于说明一个类的父类
-
父类必须是Java系统的预定义类或用户已经定义好的类。一个类只能有一个父类,但一个父类可以有多个子类。
(3) [ implements 接口名称列表 ]——implements关键字
-
implements关键字用来告诉编译器类实现的接口
-
一个类可以实现多个接口,多个接口之间用逗号分隔,其形式为:
implements interface1,interface2,...,;
-
使用接口的主要目的是为了使程序的功能描述和功能的具体实现相分离,从而使程序结构更清晰。
2、成员变量和常量——刻画对象的状态
(1)成员变量/常量的声明
[访问控制修饰符][static]<数据类型> 变量名;
[访问控制修饰符][static][final]<数据类型> 常量名;
- 成员变量/常量的定义与普通变量/常量基本一致,只是声明必须放在类体中
(2)[ 成员变量/常量的访问控制修饰符 ]
1) 各修饰符简介
- 使用访问控制修饰符可以限制访问成员变量或常量的权限
- 访问控制修饰符有4个等级:private、protected、public以及默认(即不指定修饰符)
类型 | private | protected | public | 默认 |
---|---|---|---|---|
所属类 | 可访问 | 可访问 | 可访问 | 可访问 |
同一个包中的其他类 | 不可访问 | 可访问 | 可访问 | 可访问 |
同一个包中的子类 | 不可访问 | 可访问 | 可访问 | 可访问 |
不同包中的子类 | 不可访问 | 可访问 | 可访问 | 不可访问 |
不同包中的非子类 | 不可访问 | 不可访问 | 可访问 | 不可访问 |
2)private
- private修饰的成员变量和成员方法都只能在本类中被调用,在别的类里如果想要调用它们,先要在本类中定义中间方法,在中间方法中调用私有成员变量/方法,再在别的类里调用中间方法 从而实现间接调用私有成员变量/方法
class Demo
{
private int PrivateNum = 10;//私有成员变量
public void showNum()//定义中间方法来调用私有成员变量
{
System.out.println(PrivateNum);
}
private void PrivateMethod()
{
System.out.println("成功调用私有方法");
}
public void showMethod()
{
PrivateMethod();
}
}
public class showPrivate
{
public static void main(String[] args)
{
Demo d = new Demo();
//d.PrivateNum; //错误,这样直接调用私有成员变量是不可行的
d.showNum(); //正确,间接调用私有成员变量
//d.PrivateMethod() //错误,这样直接调用私有成员方法是不可行的
d.showMethod(); //正确,间接调用私有成员方法
}
}
-
private用途
把成员变量都用private修饰,然后提供对应的
getXxx( )
方法来获取值 ,setXxx( )
方法来赋值
(3)static关键字
1)static关键字的作用
可用于修饰成员变量和成员方法
- 修饰成员变量时
- 实例变量(不加static修饰):
eg.private int x;
- 类变量(又称静态变量)(加static修饰):
eg.private static int x;
- 实例变量(不加static修饰):
- 修饰成员方法时
- 方法那一节有写 自己去看
2)static关键字的特点
-
随着类的加载而加载
-
优先于对象而存在
-
被类的所有对象共享(是否使用静态关键字的条件)
-
可以通过类名调用
- 只有被static修饰的成员变量,即静态变量才能直接通过类名来调用,类静态变量即实例变量只能通过方法名调用
class Student { public String name1 = "非静态变量"; public String name2 = "静态变量"; } class Static { public static void main(String args[]) { Student s = new Student(); System.out.println("利用对象名调用非静态变量name1:"+s.name1); //System.out.println("利用类名调用非静态变量name1:"+Student.name1); //报错:Error:(13, 55) java: 无法从静态上下文中引用非静态 变量 name1 System.out.println("利用对象名调用非静态变量name2:"+s.name2); System.out.println("利用类名调用非静态变量name2:"+Student.name2); } }
3)static关键字注意事项
-
在静态方法中是没有this关键字的
静态是随着类的加载而加载,this是随着对象的创建而存在,静态是先于对象存在,所以静态无法拥有this关键字
-
静态方法只能访问静态的成员变量和静态的成员方法
4)main方法是静态的
格式讲解:public static void main(String[] args){...}
public
:公共的,使得main方法访问权限很大static
:静态的,不需要创建对象,通过类名就可以访问,方便jvm的调用void
:无返回值main
:几乎所有语言都main为入口String[] args
:字符串数组
(4) 静态变量与成员变量的区别
静态变量 | 成员变量 | |
---|---|---|
所属不同 | 属于类,也称为类变量 | 属于对象,也成为了实例变量(对象变量) |
在内存中的位置不同 | 存储于方法区的静态区 | 存储于堆内存 |
在内存中的出现时间不同 | 随类的加载而加载,随类的消失而消失 | 随对象的创建而存在,随对象的消失而消失 |
调用方式不同 | 可以通过类名调用,也可以通过创建对象用对象名来调用 | 只能通过创建对象用对象名来调用 |
(5)成员变量与局部变量的区别
成员变量 | 局部变量 | |
---|---|---|
在类中的位置不同 | 类的内部,方法的外部 | 方法内部或者方法声明上 |
在内存中的位置不同 | 栈内存 | 堆内存 |
生命周期不同 | 随对象的存在而存在,随对象的消失而消失 | 随方法的调用而存在,随方法的调用完毕而消失 |
初始值不同 | 有默认的初始值(eg. null/0) | 没有默认的初始化值,必须先定义赋值才能够使用 |
声明位置 | 在类体中声明 | 在方法体中声明 |
3、成员方法——描述对象的行为
(1)成员方法定义
- 成员方法——事物行为
- 成员方法的定义和普通方法定义一样,只不过去掉了static
(2)在方法体中使用this关键字
1)this的定义
- this代表当前类的对象引用,简单的记,其实它就是当前类的一个对象
- 方法被哪个对象调用,this就代表哪个对象
2)this的使用
- 当成员变量的名字和局部变量的名字相同时,如果要在方法中访问成员变量,可以使用this关键字
- 在方法体中,通过this关键字可访问当前类的成员变量和方法
- 辅助调用类的构造方法,尤其是当构造方法有多个时
class Student
{
//私有成员变量name
private String name;
//公有成员方法setName(),用于给私有成员变量name初始化
public void setName(String name)
{
//name = name; 在成员变量和局部变量名字一样的时候,想要把实参的值赋给形参,就需要在成员变量前加上this关键字加以指定
this.name = name;
}
//共有成员方法getName(),用于调用私有成员变量name
public String getName()
{
return name;
}
}
public class This
{
public static void main(String[] args)
{
//创建Student类的对象s
Student s = new Student();
//通过成员方法setName()给成员变量name赋值
s.setName("洋次郎");
//通过成员方法getName()调用成员变量name
System.out.println("the name of the student is:"+s.getName());
}
}
4、构造方法
(1)构造方法的作用
- 给对象的数据进行初始化,即:用new操作符创建新对象后初始化新建对象
- 构造方法的重载可用于给成员变量赋值,例子如下所示
class Student()
{
//成员变量
private String name;
private int age;
//Student类的构造方法
public Student(String name,int age)
{
this.name = name;
this.age = age;
}
}
public class ConsructMethod
{//主方法
public static void main(String[] args)
{
Student s = new Student("洋次郎illion",35);
}
}
(2)构造方法的格式
- 方法名与类名相同,没有返回值类型(void也没有),没有具体的返回值
class Student()
{
//Student类的构造方法
public Student(参数)
{
System.out.println("这是Student类的构造方法");
}
}
(3)默认构造方法
-
我们在创建Student类的对象s的时候所用的这个语句
Student s = new Student();
其中Student()
就是一个默认构造方法 -
如果我们没有给出构造方法,系统会自动提供一个无参构造方法,连public修饰符都没有
(4)构造方法的重载
- 值得注意的是,当我们给出了多个重载的构造方法后,还想调用默认的无参构造方法
Student(){};
时,就需要自己写了,因为系统在我们给出了构造方法后将不再提供默认无参构造方法
二、对象
对象是某个类事物的具体存在,是一个具体的实例
1、创建对象
类名 对象名 = new 类名 ( ) ;
2、对象调用成员方法
对象.成员变量名 ;
3、对象调用成员变量
对象.成员方法名( ) ;
4、匿名对象
匿名对象就是没有名字的对象,是对象的一种简化表现形式
匿名对象的使用情形:
- 对象仅仅只调用一次方法的时候使用。
- 这样的好处:匿名对象调用完毕之后就成了垃圾,可被垃圾回收器回收,提高了程序的运行效率
- 所以匿名对象不合适于需要多次调用成员方法的情形
class Student
{
public void show()
{
System.out.println("这是学生类的成员方法show方法")
}
}
class NoName
{
public static void main(String[] args)
{
Student s = new Student();//新建一个名为s的对象
s.show();//第一次调用对象s对应的成员方法
s.show();//第二次调用对象s对应的成员方法
new Student().show();//新建了一个匿名对象,并且调用了这个匿名对象所对应的成员方法
new Student().show();//又新建了一个匿名对象,并且调用了这个匿名对象所对应的成员方法,注意:这里的匿名对象和前一个已经不是一个了
}
}
- 作为实际参数传递的时候使用
class Student //学生类
{
public void homeWork()
{
System.out.println("学生做的作业情况");
}
}
class Teacher //老师类
{
//老师类的giveScore成员方法
//该方法为引用传递 形参为学生类的对象s
public void giveScore(Student s)
{
s.homeWork();
System.out.println("老师打分情况");
}
}
class NoName
{
public static void main(String[] args)
{
//学生类的对象用非匿名的对象作为参数传递,演示如下
Teacher t = new Teacher();
Student s = new Student();
t.giveScore(s);
System.out.println("-------------------");
//学生类的对象用匿名对象作为参数传递,演示如下
Teacher t2 = new Teacher();
t2.giveScore(new Student());
System.out.println("-------------------");
//老师这个类对应的参数也用匿名对象的话,演示如下
new Teacher().giveScore(new Student());
}
}
三、实例分析
1、类的初始化过程
Student s=new Student();
在内存中做了哪些事情?
- 加载
Student.class
文件进内存 - 在栈内存为s开辟空间
- 在堆内存为学生对象开辟空间
- 对学生对象的成员变量进行默认初始化
eg.默认name=null;age=0;
- 对学生对象的成员变量进行显示初始化
eg.显示初始化是指在类中声明成员变量的时候就给他们赋值
- 通过构造方法对学生对象的成员变量赋值
- 学生对象初始化完毕,把对象地址赋值给s变量
*要注意的是:构造方法赋值
可以覆盖显示初始化
,显示初始化
可以覆盖默认初始化
2、什么时候将一个变量声明为成员变量
只有当这个变量是用来描述这个类的时候才把它定义为成员变量
四、封装
隐藏对象的属性和实现细节,只对外提供公共访问方式
1、封装的好处
- 隐藏实现细节,提供公共的访问方式
- 提高代码的复用性
- 提高安全性
2、封装的原则
- 隐藏不需要对外提供的内容
- 隐藏属性,提供公共访问方式
五、代码块
代码块的执行顺序:静态代码块-----构造代码块------构造方法
1、局部代码块
- 局部代码块在方法中出现
- 限定变量生命周期,及早释放,提高内存利用率
举例:
class CodeDemo
{
public static void main(String[] args
{
{
int x = 23;
System.out.println(x);
}//在局部代码块里定义的x只在局部代码块里有效,跳出局部代码块的大括号之后,x的生命周期结束
System.out.println(x);//无法输出x
}
}
2、构造代码块
- 构造代码块在类中方法外出现
- 多个构造方法中相同的代码存放到一起,对对象进行初始化,每次调用构造都执行,并且在构造方法前执行
举例:
class Code
{
public Code(){}//定义Code类构造方法
{
int x = 23;
System.out.println(x);
}//定义一个构造代码块 虽然没有被对象调用,但是在对象被初始化的时候会自动被执行,且无论构造代码块写在构造方法前面还是后面,都是先执行构造代码块,再执行构造方法
}
class CodeDemo
{
public static void main(String[] args)
{
Code c1 = new Code();//定义c1为Code类的一个对象
Code c2 = new Code();
Code c3 = new Code();
...
//构造代码块的用途:如果定义一个类的多个对象c1、c2、c3... ,而且都要对这些对象执行相同的操作,那么把这些相同的操作放到构造代码块里,在每次初始化对象的时候自动执行就可以少些重复的代码
}
}
3、静态代码块
- 静态代码块在类中方法外出现
- 并加上static修饰,用于给类进行初始化,在加载的时候就执行。并且只执行一次。
class Code
{
public Code(){}//定义Code类构造方法
static{
int x = 23;
System.out.println(x);
}//静态代码块和构造代码块很相似,只是在构造代码块大括号前边加上static,静态代码块可以理解为被static修饰的构造代码块,只在类被加载的时候执行,且只执行一次。所以常用来给类进行初始化
}