文章目录
面向对象概述
随着软件规模的增大,结构话语言的弊端逐渐暴露,开发周期长、产品质量不高等。面向对象思想开始浮现,它将所有预处理的问题抽象为对象,同时了解这些对象具有的属性和行为方式,实质上就是对现实世界中的对象进行建模操作。
类和对象
对象是事物存在的实体,如人、书桌、计算机等。一个对象通常分为两部分:静态+动态,静态部分被称为属性,如高矮、胖瘦等;动态部分就是这个对象会执行的行为和动作,如哭泣、行走、说话等。
为了描述一类物,可以将这类物的属性和行为封装起来。类实质上就是封装对象属性和行为的载体,而对象则是类抽象出的一个实例
面向对象程序的特点
- 封装性。封装是面向对象的核心思想,将对象的属性和行为封装起来,其载体就是类。类通常对客户隐藏实现细节,用户只需要用而不需要了解实现细节。
- 继承性。类与类之间可能存在关系,这种关系称为关联,如百货公司类和销售员类、学生类和教师类。继承是关联中的一种。继承可以将父类的属性和方法在子类中复用,避免重复编写代码,提高效率。
- 多态性。多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
类
Java中使用class关键字来定义类
Class 类名{
类体
}
成员变量
Java中对象的属性称为成员变量,成员变量的类型可以设置为Java中合法的数据类型
成员方法
Java中用成员方法对应于类对象的行为,成员方法的定义语法如下
权限修饰符 返回值类型 方法名(参数类型 参数名){
...//方法体
return 返回值
}
参数可以是对象,也可以是基本数据类型的变量,返回值有无均可,无时用void修饰
权限修饰符
权限修饰符可以定义一个类的被使用权限,缺省时默认为protected
private | protected | public | |
---|---|---|---|
本类 | 可见 | 可见 | 可见 |
同包其他类或子类 | 不可见 | 可见 | 可见 |
异包其他类或子类 | 不可见 | 不可见 | 可见 |
局部变量
成员方法内定义的变量。局部变量在方法被执行时创建,方法执行结束时被销毁
其有效范围称为变量的作用域,在互不嵌套的作用域中可以同时声明两个名称和类型相同的局部变量(如两个for循环都可以用int i控制循环)
this关键字
Java中用this关键字来代表本类对象的引用
类的构造方法
除成员方法外,类中还存在一种特殊类型的方法:构造方法。
构造方法是一个与类同名的方法,每当类实例化一个对象时,类都会自动调用构造方法,如果没有明确定义构造方法,编译器会自动创建一个不带参数的默认构造方法。
构造方法的特点:无返回值,方法名与类名相同
语法格式如下:
public 类名(){
... //构造方法体
}
静态变量、常量和方法
被static修饰的变量、常量和方法。可在本类或其他类中使用类名和‘.’运算符调用静态成员,静态成员也受修饰符约束。
静态方法中不可以使用this关键字
静态方法中不可以直接调用非静态方法
类的主方法
类的主方法是类的入口点,定义了程序从何开始,提供对程序流向的控制
public static void main(String[] args){
... //方法体
}
静态、无返回值、形参为数组,其中args[0]~args[n]为第1到第n个参数,可以使用args.length获取参数个数
对象
对象的创建
类名 对象名 = new 类名(构造方法的参数)
访问对象的属性和行为
创建对象后,可以用"对象名.类成员"的属性和行为
对象的销毁
Java拥有完整的垃圾回收机制,当对象处于以下两种情况时会被JVM视为垃圾:
- 对象引用超过其作用范围
- 将对象赋值为null
类的继承
继承是现有类的复用,是基于某个父类进行扩展得到一个新的子类,子类可以拥有父类的属性和方法,也可以增加新的属性和方法,或者直接重写父类中的某些方法。
java中用extends关键字标识两个类的继承关系,可以在子类中用super.方法名调用父类的非private成员方法
class Test{
public Test(){}
protected void doit(){}
protected void dosomething(){}
}
class Test1 extends Test{
public Test1(){}
protected void dosomething(){} 重写方法,可以修改返回值类型、权限(只能从小变大)等(只修改方法内容,其余都不变的叫重构)
protected void dosomethingnew(){} 新增方法
}
Test t1 = new Test();
Test1 t2 = new Test1(); Test1的构造方法中会先自动调用Test的无参构造方法(有参构造方法得用super显式调用),然后执行剩余语句
t2.doit() 调用Test的doit方法
t2.dosomething() 调用Test1的dosomething方法
Object 类
Object类是所有类的父类,是Java类层中最高层的类。
Object中的重要方法:
getClass() 返回对象执行时所在Class的实例,配合getname()方法可以获取类的名称
toString() 将一个对象返回为字符串的形式,返回一个String实例,现实中通常重写该方法
equals() equals方法默认实现方式是使用“==”比较两个对象的引用地址,因此在自定义类中都要重写equals方法
对象类型转换
向上转型
子类对象可以看做是一种特殊的父类对象(如平行四边形是一种四边形),因此可以将子类对象赋值给父类类型的变量,这种技术叫做向上转型,将具体的类转换为抽象的类,但这样会丧失原子类新增成员变量和方法的使用权限
class 四边形{
public static void draw(四边形 q){}
}
public class 平行四边形 extends 四边形{
public static void main(String[] args){
平行四边形 p = new 平行四边形();
draw(p)
}
}
向下转型
向下转型是将抽象的类转换为具体的类,这种需要显式转换,否则会报错
class 四边形{
public static void draw(四边形 q){}
}
public class 平行四边形 extends 四边形{
public static void main(String[] args){
四边形 q = new 平行四边形(); 向上转型
平行四边形 p = (平行四边形) q; 向下转型
}
}
判断对象类型
在向下转型时,若父类的实例不是子类的实例,则会报错,因此在向下转型之前需要先判断一下,可以用instanceof实现。
instanceof用于判断一个类是否实现了某个接口,也可以用于判断一个实例对象是否属于一个类
四边形 q = new 四边形();
if(q instanceof 平行四边形){
平行四边形 p = (平行四边形) q
}
方法重载
方法重载就是允许一个类中存在一个以上的同名方法,但这些方法的参数个数或类型不同或参数顺序不同
可以通过定义不定长参数实现方法重载,这个参数是一个数组,语法如下:
返回值 方法名(参数类型...参数名称)
public static int add(int...a){
int s = 0;
for(int i=0; i<a.length; i++){
s += a[i];
}
return s;
}
多态
多态就是不同对象对同一物体或事件发出不同的反应或响应。比如stuendt是一个父类,那么在操场上上体育课的学生和在教室里面的学生就是它的子类。这时上课铃声响了,上体育课的学生去操场,在教室里面上课的学生则是回教室,不同的学生有着不同的反应,这就是多态。
实现多态需要三个条件:
继承
重写:子类重写父类的某些方法
向上转型:父类对象的引用要指向子类对象
先有类a继承自A,A A = new a()
满足上述三个条件后,调用实例A(实际是子类a的实例)的某些方法时,会自动调用子类中重写过的方法,从而实现不同类的对象针对同一函数调用做出不同的反应。也就是说,调用A的fun1时,会自动调用a中重写的fun1。
抽象类
在继承树中,越上方的类越抽象,如鸽子–>鸟–>动物。在多态中,我们并不需要实例化父类,只需要其子类的对象,因此Java中抽象类不可以实例化,其语法如下:
public abstract calss Test{
abstract void function(); //抽象方法
}
用abstract关键字修饰的类称为抽象类,修饰的方法称为抽象方法。
抽象方法没有方法体,该方法本身没有任何意义,除非被重写。抽象方法就是说了要干啥,抽象方法的实现就是明确这事怎么干。
类中只要有一个抽象方法,该类就被标记为抽象类
抽象类被继承后,子类必须实现抽象类中的抽象方法
那么问题来了,有些子类可能并不需要父类中的所有抽象方法,但他又必须实现他们,这样就会造成代码冗余。为了应对上述问题,接口的概念便出现了。
接口
接口可以看做是纯粹的抽象类,接口中的所有方法都是抽象方法。
接上节最后的问题,可以将非全员必须的抽象方法封装到一个接口中,需要该方法的子类在继承父类的同时实现这个接口。
接口的定义语法如下:
interface drawTest{
//接口内的方法,可以省略abstract关键字,但所有方法都是public abstract的
//1 接口中变量自动是 public、static、final
//2 接口中的方法默认是 public abstract
//3 接口也产生class文件
//4 接口中的方法不能被static和final修饰,因为要重写所有接口中的方法
//5 接口中没有构造方法
void draw();
float sum(float x, float y);
}
一个类实现接口的语法
public static 子类名 extends 父类名 implements 接口名{
@Override //@Override是伪代码,表示重写(当然不写也可以)
public void draw(){}
@Override //@Override是伪代码,表示重写(当然不写也可以)
public float sum(float x, float y){}
}
类的高级特性
Java类包
Java中的每个类经过编译后会生成一个.class文件。程序规模扩大后,很容易出现类名冲突的现象。为解决这个问题,Java提供类包机制管理类文件。
完整的类路径:包名+类名,如Math类的完整名称是java.lang.Math
在类中定义包名的语法
package 包名
class{
}
package表达式必须是文件中的第一行非注释代码
包名的命名规则是全部使用小写字母
Java中的包名设计应该与·文件系统结构相对应,如一个包名为com.lzw,那么该包中的类应该位于com文件夹下的lzw子文件夹下。没有定义包的类会被归纳在默认包中
为类指定包名后,包名会成为类名的一部分,意味着使用类时必须指定全名,如com.lzw.Dog
使用import关键字导入包
import com.lzw.* //导入包中的所有类
import com.lzw.Math //导入包中的特定类
import static 静态成员
final关键字
final关键字可以用于修饰变量、方法和类,被修饰的变量值不可变,方法不可被重写,类不可被继承
final关键字定义的变量必须在声明时就对其赋值。
final修饰的对象引用只能指向当前对象,不能指向其他对象
内部类
在类中定义的类,内部类分为成员内部类、局部内部类和匿名类
成员内部类
public class Outerclass{
private class Innerclass{
...
}
}
在内部类中可以随意使用外部类的成员方法及成员变量(即使是private),但反过来不行
内部类只能在外部类的非static方法或外部类内实例化,若要在外部类外实例化内部类对象,需要用到外部类的实例,如out.new Innerclass()
内部类向上转型为接口
提供一个接口,在接口中声明一个方法。
如果在实现该接口的内部类中实现该接口的方法,就可以通过定义多个内部类来多次不同地实现接口中的同一个方法,可以在一个类中做出多个不同的响应事件,该技巧在Swing编程中常见
用this关键字获取内部类与外部类的引用
若外部类中定义的成员变量与内部类的成员变量名称相同,可以使用this关键字进行处理
class TheSameName{
private int x;
private class Inner{
private int x = 9:
public void doit(int x){
x++; //形参x
this.x++; //内部类的x
TheSameName.this.x++; //外部类的x
}
}
}
局部内部类
在类的方法或任意作用域中均可以定义内部类,在该作用域外不能访问该内部类
匿名类
在方法return时返回一个new语句,就会得到一个匿名类,匿名类编译后会产生“外部类名$序号”为名称的.class文件
静态内部类
用static修饰的内部类,静态内部类的最大特点是不可以使用外部类的非静态成员
如果创建静态内部类的对象,不需要其外部类的对象
内部类的继承
public ClassA{
class ClassB{
}
}
public class OutputInnerClass extends ClassA.ClassB{ //继承内部类ClassB
public OutputInnerClass(ClassA a){ //继承内部类时,必须给予这个类一个带参数的构造方法,且该构造方法的参数为需要继承内部类的外部类的引用,同时在构造方法体中使用a.super(语句)
a.super();
}
}