5.1 类和对象
5.1.1 类和对象定义
类:是对某种事物的抽象描述(泛指自然界某一类事物)
实例(对象):是指真实存在的(通过new创建,具体某个事物)
5.1.2 类的组成
成员变量
行为方法
构造器 ----->初始化
初始化块 ----->类初始化时,要做的事情
内部类
5.1.3 类的定义格式
[修饰符] [类名] {
0-N 成员变量
0-N 行为方法
0-N 构造器
0-N 初始化块
0-N 内部类
}
5.1.4 类修饰符
①权限修饰符(2选1):public 、 [默认] //包权限修饰符(不填)
②(2选0-1):abstract //抽象 、 final //最终,不可变
Ps:在同一个java文件,最多定义一个public类,这个public类必须个文件名相同
5.2 成员变量与局部变量
5.2.1 成员变量
成员变量是指在类中定义的变量,也就是指属性,其作用域是在整个类中有效。成员变量在定义时可以不指定初始值,系统可以按默认原则初始化,具体初始化原则如下:
基本类型的默认值为 0,boolean类型为false,复合类型的默认值为null
Byte = 0 Short = 0 Int = 0 Long = 0L Boolean = false
Float = 0.0f Double = 0.0d Char =‘\u0000’ 其他类型(引用类型)= null
(1)初始化:可以不初始化,系统会自动初始化
(2)作用域:整个类当中都能使用(无论从哪里开始定义)
(3)成员变量可以放置在类中的任何位置,不能放置在方法或初始化块中
(4)除了abstract不能用,其他修饰符都可以用
5.2.2 局部变量
局部变量一般是指在方法体内部定义的变量,其作用域是在方法块内部有效。局部变量在使用时,必须先初始化然后才能使用,否则程序则不能通过编译,错误示例代码如下:
public class Test{
public void f1(){
int i; // 变量i未初始化,编译将产生错误
// int i = 0; 正确的使用方法
System.out.pringtln(i);
}
}
- 形参:
初始化:不作初始化
作用域:代码块开始到结束
- 方法中定义的变量:
初始化:定义时必须显式初始化
作用域:从变量定义并 初始化 开始到 方法结束
- 代码块中的变量: 如:for(int i=0;i<n;i++){ } 中的i
初始化:定义时必须显式初始化
作用域:代码块开始到结束
5.2.3 成员变量和局部变量的区别
- 修饰符不同:
成员变量可以被 public,protect,private,static等修饰符修饰,而局部变量不能被控制修饰符及 static修饰;两者都可以定义成final型。
- 存储的位置不同:成员变量存储在堆,局部变量存储在栈。
- 作用域不同:
局部变量的作用域:仅限于定义它的方法,在该方法的外部无法访问它。
成员变量的作用域:在整个类内部都是可见的,所有成员方法都可以使用它。如果访问权限允许,还可以在类的外部使用成员变量。
- 生存周期不同:
局部变量:生存周期与方法的执行期相同。当方法执行到定义局部变量的语句时,局部变量被创建;执行到它所在的作用域的最后一条语句时,局部变量被销毁。
成员变量:如果是实例成员变量,它和对象的生存期相同。而静态成员变量的生存期是整个程序运行期。
(5)默认值:
成员变量:有默认值,基本类型的默认值为 0,复合类型的默认值为null。(被final修饰且没有static的必须显式赋值)
局部变量:不会自动赋值,所以局部变量在定义后先要赋初值,然后才能使用。
(6)优先级:
局部变量可以和成员变量同名,且在使用时,局部变量具有更高的优先级。
5.2.4 变量初始化的顺序
[1]变量定义位置------>[2]初始化块------->[3]构造器
例:
5.3 方法
5.3.1 定义
(1)格式:
void类型无参方法: 修饰符 void 名字(){执行代码}
void类型有参方法: 修饰符 void 名字(参数列表){执行代码}
有类型无参方法: 修饰符 数据类型 名字() { 执行代码; return 值;}
有类型有参方法: 修饰符 数据类型 名字(参数列表) { 执行代码; return 值; }
例子:
//参数列表为不确定参数个数
Public void test(String...args){ }
//数组作参数
Public void test(String[] args){ }
(2)有参方法的返回值:
- 必须与方法类型匹配或它的子类
- 无分支语句就一个return,有则可多个return
- 可返回字面值、变量、表达式、方法(或多个方法结合)
修饰符:
final、abstract只能选一、其他的修饰符都可以用
(3)构造方法:
无参构造方法:修饰符 名字(){执行代码}
//每个类只能有一个
有参构造方法:修饰符 名字(参数列表){执行代码}
//每个类可以有多个有参构造方法,但是参数类型必须不一样
跟类名相同
不能指定返回值,void也不能要
修饰符:public 、 protected 、 [默认] 、private
5.3.2 使用
(1)方法定义与使用的流程:①定义方法 ②写代码 ③调用运行
(2)可变参数:
方法的可变参数表示多个同类型的参数;可变参数只能写在参数列表的最后一位;传递的实际参数也可以是数组。
例子:public class A7_MethodParameters{
public static void main(String[] args){
String[] str={"请","叫","我","富","豪","!"}; //自定义String数组
A7_MethodParameters amp = new A7_MethodParameters();
System.out.println("============可变参数多个String=============");
amp.method(20,1000000,"请","叫","我","富","豪","!");
System.out.println("=============可变参数使用数组===============");
amp.method(25,9000000,str);
}
//该方法中的参数列表使用了可变参数String...name
public void method(int age,double money,String...name){
System.out.println("年龄: " + age);
System.out.println("资产: " + money + "亿美元");
for(String n : name){ //循环遍历输出可变参数
System.out.print(n + " ");
}
System.out.println();
}
}
5.3.3 值传递
值复制:值复制后修改值不影响原来的值
(基本类型的数据拷贝的是数据,在方法栈退出后,上一栈的传入参数不受影响)
地址赋值:地址复制后修改地址中的值,该地址的值改变
(引用类型的数据拷贝的是引用)
5.3.4 方法的重载
(1)概念与定义格式
同一个类内,方法名字一样,参数不一样(同类里方法重载)
如:public void f(){执行代码}
public void f(int i){执行代码}
public void f(int i,String s){执行代码}
public void f(String s,int i){执行代码}
(2)特性
- 参数类型不同,构成重载
- 参数个数不同,构成重载
- 参数顺序不同,构成重载
- 与返回值类型无关
- 与访问修饰符无关
Ps:注意包装类与基本类型作为参数的时候,两个类型不属于同一种类型,可以作为方法重载的两种不同的参数类型。
如:Integer 和 int
(3)与方法重写的区别
位置 | 方法名 | 参数 | 返回值 | 访问修饰符 | |
方法重写 | 子类 | 相同 | 相同 | 相同或小 | 不能比父类更严格 |
方法重载 | 本类 | 相同 | 不同 | 相同或不相同 | 相同或不相同 |
5.3.5 递归方法
- 阶乘问题
//递归计算5!
class CountTest{
public static void main(String[] args){
CountTest ct = new CountTest();
System.out.println("5!="+ct.count(5));
}
//递归计算5!
public int count(int a){
if(a == 1){
return 1;
}else{
return count(a-1)*a;
}
}
}
- 汉诺塔问题
/*
4、汉罗塔问题
*/
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class A6_TaskFourHanoi{
public static void main(String args[]) throws Exception {
int n;
//通过IO流来实现键盘输入
BufferedReader buf =
new BufferedReader(new InputStreamReader(System.in));
System.out.print("请输入盘数:");
//将盘的个数从键盘输入,然后记录下来
n = Integer.parseInt(buf.readLine());
A6_TaskFourHanoi hanoi = new A6_TaskFourHanoi();
hanoi.move(n, 'A', 'B', 'C');
}
//递归方法,先找到n==1时候的最内层,再一层一层往外。
public static void move(int n, char a, char b, char c) {
if (n == 1)
System.out.println("盘 " + n + " 由 " + a + " 移至 " + c);
else {
move(n - 1, a, c, b);
System.out.println("盘 " + n + " 由 " + a + " 移至 " + c);
move(n - 1, b, a, c);
}
}
}
5.4 构造方法与语句块
5.4.1 构造方法
1.1this:看上去,用来区分局部变量和成员变量同名的情况
1.2this:也可以代表本类对象,this代表他所在函数(方法)所属对象的引用
1.3构造方法之间的调用只能通过this语句来完成
1.4构造方法之间进行调用时,this语句只能出现在第一行,初始化要先执行,如果初始化中还有初始化,那就先执行最内层初始化,再往外执行
1.5构造方法不能交叉调用(即不能在二调用三的同时,三调用二),且每个构造方法只能调用一个构造方法
(1)定义格式:修饰符 类名 ( 形参列表 ){ }
(2)特性:
①构造器的命名:与类名相同
②构造器不能有返回类型,也不能有void
③构造器也是一种特殊的方法
④不能写return在构造器内
⑤有返回一个对象this的引用
⑥构造器可以有0-N个
⑦普通方法能与构造器同名,但一定要按照方法规则
⑧如果没有指定构造器,系统会自动创建一个”参数为空”的构造器
⑨如果有指定的“有参”构造器,则需要自己定义“参数为空”的构造器
⑩普通方法内不能用this( )调用构造器
5.4.2 语句块
对象一建立就运行了,而且优于构造方法执行
2.1作用:给对象初始化
2.2与构造方法的区别:
①构造方法使对应的对象进行初始化
②构造代码块是给所有对象进行统一的初始化
2.3构造代码块中定义的是不同对象共性的初始化内容
2.4方法内的代码块只在方法被调用时执行
(1)定义格式: 静态语句块 static{ 执行代码 }
非静态语句块 { 执行代码 }
(2)语句块的应用场合:当已调用类时,就希望该类执行一些代码,就可以用语句块
(3)语句块里能写: 常量(不能用static修饰)
变量
条件语句
运行普通方法
类(不能添加修饰符)
语句块(非静态语句块定义数据)
(4)静态语句块与非静态语句块区别
静态语句块在非静态语句块之前运行,在类编译时已经编译运行了
静态语句块里运行的代码都是静态的,在内存里有固定的位置
(5)构造方法和语句块的异同
相同:应用场合相似,都是一调用这个类就可以执行一些代码
不同:1.语句块在构造方法之前运行
ps:构造方法中的super()在非静态语句块之前被调用
2.构造方法可以在调用时传参,语句块不可以
5.5 类成员
static 关键字修饰的成员就是类成员,类成员有类变量、类方法、静态初始化块三个成分,static 关键字不能修饰构造器。static修饰的类成员属于整个类,不属于单个实例
5.5.1 理解类成员
定义:static修饰的成员称类成员。
Ps:可以通过类引用访问,但推荐直接类访问。因为类引用访问是通过该类来访问。类成员(包括方法、初始化块、内部类、枚举类)不能访问实例成员(包括成员变量、方法、初始化块、内部类、枚举类)。因为类成员属于类的,类成员的作用域比实例成员大,而且完全可能类成员实例化完成了,实例化成员还没实例化。
- 类变量
①定义:static修饰的成员变量称为类变量
②调用(不需new):类名.类变量 (在本类内调用可直接 类变量)
- 类方法
①定义:static修饰的方法称为类方法
②调用(不需new):类名.类方法 (在本类内调用可直接 类方法)
Ps:因为类成员是所有实例都共用的,要注意值修改问题
当使用引用来访问类成员时,实际上依然是委托给该类来访问类成员,因此,即使某个引用为null,它也可以访问它所属类的类成员。如:
public class A9_ClassMumber{
//static修饰的类方法
public static void test(){
System.out.println("static修饰的类方法");
}
public static void main(String[] args){
//定义一个A9_ClassMumber变量,其值为null
A9_ClassMumber cm=null;
//使用null对象调用所属类的静态方法(可成功调用)
cm.test();
}
}
上面程序可以正常编译、执行,成功调用static修饰的类方法,这表明null对象可以访问它所属类的成员。
- 静态初始化块
在类初始化阶段,系统会调用该类的静态初始化块来对类进行初始化。一旦给类初始化结束后,静态初始化块永远不会再执行。
5.5.2 单例(Singleton)模式
- 定义:如果一个类始终只能创建一个实例,则这个类被称为单例类
- 分类:①懒汉式、②饿汉式
①懒汉式
例子:public class A9_SingletonTest{
public static void main(String[] args){
//创建Singleton对象不能通过构造器
//只能通过getInstance方法来得到实例
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//因为只能创建一个对象,所以引用的地址一样
System.out.println(s1 == s2);
}
}
//单例类:懒汉式
class Singleton{
//使用一个类变量来缓存曾经创建的实例
private static Singleton instance;
//对无参构造方法使用private,隐藏该构造方法
private Singleton(){
}
//提供一个静态方法,用于返回Singleton实例,
//该方法可以加入自定义控制,保证只产生一个Singleton对象
//将不会重新创建新的实例
public static Singleton getInstance(){
if(instance == null){
//创建一个Singleton对象,并将其缓存起来
instance = new Singleton();
}
return instance;
}
}
②饿汉式
例子://单例模式:饿汉式
public class A11_SingletonTest{
public static void main(String[] args){
//创建Singleton对象不能通过构造器
//只能通过getInstance方法来得到实例
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
Singleton instance3 = Singleton.getInstance();
//因为只创建了一次对象,所以引用的地址一样
System.out.println( (instance1 == instance2) );
System.out.println( (instance1 == instance3) );
}
}
//单例类:饿汉式
class Singleton{
//使用一个类变量来创建一个实例
private static Singleton instance = new Singleton();
//对无参构造方法使用private,隐藏该构造方法
private Singleton()
}
//提供一个静态方法,用于返回Singleton实例,
public static Singleton getInstance(){
return instance;
}
}
5.6 继承
作用:为了类的复用、和扩展而设计的
5.6.1 格式
类与类 : 类名 extends 要继承的类名
接口与接口: 接口 extends 要继承的接口名,要继承的接口名,......
5.6.2 特性
(1)用在类与类或者接口与接口之间
(2)一个对象继承另一个对象之后,这个对象就具有了另一个对象的全部特征,当然还要根据访问权限,private修饰的无法访问,默认修饰符的也只能是同包的子类访问
(3)注意:在类与类之间,不能同时直接继承多个,可以间接继承;在接口与接口之间可以。
(4)当子类和父类的数据相同时,父类的数据将会被子类数据覆盖掉。但不能覆盖无参构造方法和语句块,即子类和父类的无参构造方法和语句块同时存在。
(5)如果要调用所继承类里的数据,我们可以用super.要调用的数据,但super.不能调用构造方法、语句块和内部事物
(6)子类继承父类,子类中可以用的父类属性(非静态)、方法(非静态)都是父类的引用中的属性、方法(即子类使用的父类属性、方法属于父类引用的)
(7)final修饰的类不能被继承
(8)子类和父类都有的用子类,子类没父类有的用父类;但调用父类独有的方法时,该方法中使用的属性父类、子类都有,则还是默认调用父类的属性。(父类的东西可以理解为子类自己的东西)
例如://输出结果为name = "Parent"的值
public class A14_ExtendsGetValue{
public static void main(String[] args){
Child c = new Child();
System.out.println( c.getName() ); //子类调用父类独有的方法
}
}
//父类
class Parent{
protected String name = "Parent"; //父类的属性
public String getName(){ //父类独有的方法
return name; //返回的的父类的属性
}
}
//子类
class Child extends Parent{
protected String name = "Child"; //与父类相同名字的属性
}
5.6.3 子类与父类构造器的调用规则
(子类代表当前类,父类代表所继承的类)
(1)子类和父类都有无参和有参构造方法的情况:
①用无参(有参)构造调用子类,会先运行父类无参构造方法,
再运行子类无参(有参)构造方法。
(2)父类只有有参构造方法,子类有无参和有参构造方法的情况:
①要在子类的无参和有参构造方法里的第一行加上:super(参数);
②用无参(有参)构造调用子类,会先运行父类有参构造方法,
再运行子类无参(有参)构造方法。
5.6.4 方法重写
(1)在类里,如果要改写继承过来的普通方法,可以用方法重写,步骤如下:
①先点击右键,选择Sourse下的Override选项
②再选择你要改写的方法,直接在生成方法里写代码
- 方法重写:在子类中覆盖了父类的方法的条件
两同两小一大:①方法名相同
②参数列表完全相同
③返回值类型比父类返回值类型小或相同(子类重写方法的返回值类型必须为父类方法的返回值类型,或是父类方法返回值类型的子类类型)
④子类方法声明抛出的异常比父类抛出的异常小或相同
⑤子类方法的修饰符权限比父类的大或相同(父类方法需非private)
(静态方法不能被覆盖成非静态方法)
(3)构造方法不能被继承,因此不能被重写
①在子类中调用父类构造方法:
格式:
super(); //只能是构造方法中的第一条,用于调用父类的一个构造方法
super(参数);
Ps:1.没写super()时,子类构造方法中第一行默认调用父类构造方法。
2.当子类构造方法被调用时,父类以及父类的父类等等,都会被调用,直到最顶 层的父类,其构造方法都被调用。
②子类中调用父类属性和方法:
格式:
super.父类属性或方法 //区别于子类本身的属性、方法调用
例如:父类、子类都有同一个属性 String name时
super.name; //调用父类的name
name; //调用子类的name
5.6.5 this与super
(1)this
是指向本类的实例的引用
当前类的调用,通过this可以调用当前类的量和方法
(this不能在静态方法里使用)
不管是否写this,在访问本类成员时,都默认已加上this
方法内不能使用this()调用构造器
例1:
//this 访问本类非静态成员
class A{
int score;
public void test(){
this.score;
this.test2();
}
public void test2(){
}
}
例2:
//this调用本类的构造器
Class B{
int age;
public B(){
}
public B(){
this(); //调用无参构造器
}
}
例3:
//在一个方法中返回 this, 可供外界获取这个实例的引用
public class ThisTest
{
int count;
//increase()方法返回this 即本方法体
public ThisTest increase(){
count ++;
System.out.println( "increase[1] --> count: "+ count );
//在一个方法中返回 this, 可供外界获取这个实例的引用
return this;
}
//increase2()方法返回this 即本方法体
public ThisTest increase2(){
count += 2;
System.out.println( "increase[2] --> count: "+ count );
return this;
}
//主方法
public static void main(String[] args){
ThisTest tt = new ThisTest().increase().increase2().increase();
System.out.println( "count: "+ tt.count );
}
}
(2) super和this的异同
1)super(参数):调用基类(父类)中的某一个构造函数(应该为构造函数中的第一条语句)
2)this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
3)super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)
4)this:
在类的方法定义中使用this关键字代表使用该方法的对象的引用,this可以看做一个变量,值是当前对象的引用;它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
5)调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
6)super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。
7)super()和this()均需放在构造方法内第一行。
8)尽管可以用this()调用一个构造器,但却不能调用两个。
9)this()和super()不能同时出现在一个构造函数里面,因为this()必然会调用其它的构造函数,其它的构造函数必然也会有super()语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
10)this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
11)从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
5.7 多态
5.7.1 概念及特性
(1)多态概念:用基类的引用指向子类的对象
(2)多态必备条件:①编译类型与运行类型不同
②拥有继承关系的类
(3)多态的作用:方便后期扩展类的使用而设计
(4)多态的两个好处:
①应用程序不必为每个派生类编写功能调用,只需要对抽象基类进行处理即可。大大提高程序的可复用性。//继承
②派生类的功能可以被基类的方法或引用变量所调用,这叫后兼容。可以提高可扩充性和可维护性。 //多态的真正作用
(5)多态在什么地方用:①方法的参数中
②方法的返回值类型中
Ps:引用变量只能调用声明该变量时所用类里包含的方法,如:Object p = new Person();
引用变量p只能调用Object类的方法,而不能调用Person类里定义的方法。
5.7.2 引用变量的强制类型转换
①用处:
父类引用 = 子类引用; //自动转化
子类引用 = (子类)父类引用; //需要强转
例子:class Dog extends Animals{
Public static void main(Stringp[] args){
Dog dog = new Dog(); //实例化Dog类引用dog
Animals animal_animal = new Animals(); //实例化Animals类
Aniamls animal_dog = new Dog();//多态,Animals类引用Dog类实例化
// dog = (Dog)animal_animal; //报错,因为运行类型为Animals类
dog = (Dog)animal_dog; //正确,因为运行类型为Dog类
}
}
①作用:如果需要让多态实例化出来的引用变量调用它运行时类型的方法,则必须把它强制类型转换成运行时类型,强制类型转换需要借助于类型转换运算符。
②类型转换运算符用法:(type)variable //可将一个引用类型变量转化为其子类类型
③引用类型之间的转换条件:只能在具有继承关系的两个类之间进行。
5.7.3 instanceof 运算符
(1)格式: boolean b = A类对象 instanceof B类或接口 ; //返回boolean型
(2)作用: 判断“前面的对象”是否是“后面类、其子类、接口”的实例;一般用于强制类型转换之前的判断,这样会更加安全,同时保证程序更加健壮。
5.8 封装
5.8.1包
import作用:添加一个import导入你要关联的对象所在包。
- 创建包:
①规则:都为小写字母,一般如:com.java //每个.就一层文件夹
②定义格式:package 包名; //定义包,必须在源文件的第一行
例如:package com.java;
- 导入包:
① 定义格式:import 包名.类名/*;
例如:import java.util.Scanner; //导入util包中的Scanner类
import java.util.*; //导入util包中的所有类
import java.util.System.out; //导入util包中System类中的out方法
② 直接使用:
例如:java.util.Arrays.toString(arr); //将整个数组转为String型
- 静态导入:
格式:import static 包名;
例如:import static java.lang.*; //只能使用lang包的静态成员
特性:只能使用该包内的静态属性和静态方法