目录
前言
主要介绍面向对象思想、Java类的创建和对象的实例化、static和this关键字、Java面向对象的三大特征的实现、以及super和final关键字。
一、面向对象编程的思想
程序设计中最为主要的两类思想便是面向过程的程序设计思想和面向对象的程序设计思想,简单来说:
面向过程的程序设计思想中,编写程序的基本单元是函数,一个函数实现一个功能,以此来实现代码的模块化和复用。突出的是实现功能的步骤和每一步之间的因果关系。
面向对象的程序设计思想中,初始元素是对象,然后将具有共同特征的对象归纳成类。以类为设计的基本单元,更符合现实生活中人们处理问题的思维方式。并且能降低不同功能程序之间的耦合度,也就是关联程度,从而也提高了程序的扩展性。
基于面向对象的思想,软件开发又可包含三个编程思想 ,分别是:
OOA(Object-Oriented Analysis):面向对象分析
OOD(Object-Oriented Design):面向对象设计
OOP(Object-oriented programming):面向对象编程
二、类的创建
一个类=属性+方法,这个属性就是成员变量,主要描述状态。方法则主要描述动作。
定义类的格式:
[修饰符列表] class 类名{
类体;
}
修饰符可以是public也可以不写。当修饰符是public时代表它是公共类,此时该类名必须与该Java源程序的文件名一致,否则编译报错。同一个Java源文件中可定义多个类,但只能有一个类使用public修饰。多个类的源文件编译后会生成对应的多个字节码文件。
注意:类名的规范是单词首字母大写
三、类的实例化
对象就是类的实例化,是类的具体表现。而类则是对象的抽象。
1.创建对象的格式:
类名 变量名 = new 类名();
变量名前面的类名代表自定义的类和基本数据类型比如int一样,是一种数据类型。
几乎所有对象的创建都需要通过new操作符来完成。通过new创造出来的对象都会在堆区开辟一块空间,此时该变量中存的是创建的对象在堆中的地址,该变量也叫做引用,而不是对象本身。
2.构造方法(constructor)
格式里的 类名() 就是调用的构造方法。
构造方法是类中的一个特殊方法,它的作用就是实现对象的创建和属性的初始化。 该方法通过new操作符来调用。
格式为:
public 类名(形式参数列表){
方法体;
}
注意:构造方法没有返回值,并且方法名必须是类名
当一个类中没有写任何构造方法时,系统会提供一个默认的没有参数的构造方法, 给属性赋默认值,这个默认的构造方法叫做缺省构造器。
当一个类中有了构造方法(无论提供的是有参还是无参的)后,系统就不再提供缺省构造器。构造方法支持重载,一般会写一个无参的构造方法和其它的有参的构造方法。
整形(byte,short,int,long) | 0 |
浮点型(float,double) | 0.0 |
char | 0/‘\u0000',编码0,不是字符0 |
boolean | false |
引用数据类型 | bull |
public class Test01 {
public static void main(String[] args) {
//创建对象
Test02 t2 = new Test02();//系统默认的构造方法
System.out.println(t2.age);//赋默认值
System.out.println(t2.name);
// 因为Test03中并没有无参数的构造方法,只有一个有参数的构造方法,
// 此时JVM也不会提供缺省构造器,因此执行该行代码会报错
//Test03 t3 = new Test03();
Test03 t4 = new Test03(100,20);
}
}
class Test02{
int age;
double name;
}
class Test03{
int num;
int age;
//无参数的构造方法
//JVM会将所有参数赋上默认值
/*public Test03() {
}*/
//有参数的构造方法
//用传入的参数对属性初始化
public Test03(int num, int age) {
this.num = num;
this.age = age;
}
}
所有实例变量均在构造方法中完成初始化。
四、static和this关键字
1.static关键字
static关键字用来修饰变量和方法,所修饰的变量叫做静态变量,修饰的方法叫做静态方法。该关键字修饰的变量和方法都是类级别的,访问一般用类名.的方式,对象也可以访问,但是不建议。因为会将静态变量和实例变量混淆。
没有用static关键字修饰的变量叫实例变量,没有用static修饰的方法叫做实例方法。实例变量和实例方法与类实例化的对象绑定,只能通过引用.的方式,或者直接通过new对象的方式来访问,无法通过类名访问。
class Test02{
int age;
String name;
static String nationality = "中国";//static修饰的变量一般为该类对象的共性
//静态方法
public static void doSome(){
System.out.println("静态方法");
}
//实例方法
//和实例变量类似,在返回类型前没有用static修饰的就是实例方法
public void doOther(){
System.out.println("实例方法");
}
public Test02() {
}
public Test02(int age, String name, String nationality) {
this.age = age;
this.name = name;
this.nationality = nationality;
}
}
static修饰的变量一般为该类对象的共性,存储在方法区中,在类加载时初始化。
注意:空指针异常NullPointerException一般是对空指针的访问引起的,比如将某个引用赋为null,再去访问它的属性和方法,就会报错。但是如果访问的是静态的变量或者方法,则会自动转换为类名.的方式,不会报错。
静态代码块
class Test04{
static{
System.out.println("这是静态代码块");//定义在类之中,在类加载时,main方法之前执行,只执行一次,
}
}
实例代码块
class Teat04{
{
System.out.println("这是实例代码块");//定义在类之中,在构造方法之前执行。构造方法每执行一次,实例代码块就运行一次
}
}
2.this关键字
this关键字可以看成是一个变量,一个引用。保存的是当前对象的地址,指向自身,它存储在堆中创建的对象内。this只能用在实例方法和构造方法中。一般我们都会在构造方法中用this.的方式访问属性,因为为了增强可读性,会使形式参数的变量名与要赋值的属性名一致,此时的this关键字就不可省。
class Test05{
String name;
public Test05() {
this(null);//在一个构造方法中调用另一个构造方法,使用this()。
//this()只能出现一次,且只能出现在代码的第一行。
}
public Test05(String name) {
this.name = name;
}
}
在一个构造方法中调用另一个构造方法使用this(可加参数)的方式,但只能出现一次,且只能出现在第一行。
五、面向对象的三大特征
1.封装
封装是指将现实世界中存在的某个客体的属性与行为绑定在一起,并放置在一个逻辑单元内。该逻辑单元负责将所描述的属性隐藏起来,外界对内部属性的所有访问只能通过提供的接口(方法)实现。封装的好处是实现对属性的保护,降低程序复杂性,更有利于维护程序。
Java的封装通过private关键字和setter和getter方法对属性的私有化来实现。
private表示私有的,被private修饰的变量只能在在本类中直接访问。外部访问通过我们定义的setter和getter方法。setter方法用来修改属性,getter方法用来获取属性。
getter方法的格式:
public 该属性的数据类型 get+属性名称(首字母大写)( ){
return 该属性;
}
setter方法的格式:
public void set+属性名称(首字母大写)( 该属性的数据类型 形参变量名){
该属性 = 形参变量;//可对修改的值添加一系列判断和约束条件
}
public class Test01 {
public static void main(String[] args) {
Test03 t = new Test03(100,120);
//t.num;直接访问会报错
System.out.println(t.getNum());//100
t.setNum(200);
System.out.println(t.getNum());//200
}
}
class Test03{
//用private修饰的变量只能在本类中直接访问,外部只能通过方法访问
private int num;
private int age;
//有参和无参的构造方法
public Test03() {
}
public Test03(int num, int age) {
this.num = num;
this.age = age;
}
//getter和setter方法
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
每个类的属性都最好封装,并提供setter和getter方法
2.继承
1.继承概述
继承在现实世界中很常见,在面向对象的设计中主要指的是派生类从基类获得了属性和方法。
Java中继承使用extends关键字,且类不支持多继承,只能是单继承。
格式: [修饰符列表] class 类名 extends 继承的类名{
类体;
}
除构造方法外都可以被继承,但基类私有的属性和方法则不能在派生类中直接访问。
所有的类都会直接或间接的继承Object类,Object是所有类的根类。即使该类没有继承任何类,也会默认继承根类。
public class Test04 {
public static void main(String[] args) {
Test06 t6 = new Test06();
//派生类继承了基类,拥有了基类的属性和方法
t6.doSome();//输出这是基类的方法
}
}
class Test05{
private String name;
public Test05() {
}
public Test05(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void doSome(){
System.out.println("这是基类的方法");
}
}
class Test06 extends Test05{
//Test06继承了Test05,Test06就称为派生类,Test05就是基类
private int age;
public Test06() {
}
public Test06(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void fun(){
String n = getName();//派生类可直接访问基类的方法
}
}
但是,继承的缺陷在于,它加强了派生类和基类之间的耦合度,对基类的改变也会影响派生类。 如此可见,继承并不是将基类中的代码直接复制到派生类当中,
2.super关键字
与this关键字类似,super表示该类的基类,也只能用在构造方法和实例方法中,不能用于静态方法。由于派生类可直接访问基类除构造方法、私有方法和私有属性外的所有方法和属性,因此大部分情况下super可以省略。
super()的作用是在派生类的构造方法中调用基类的构造方法,完成对基类属性的初始化。同样,super()也只能出现一次,且只能出现在构造方法的第一行。很明显,super()和this()不能同时存在。
class Test05{
private String name;
public Test05() {
}
public Test05(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void doSome(){
System.out.println("这是基类的方法");
}
}
class Test06 extends Test05{
//Test06继承了Test05,Test06就称为派生类,Test05就是基类
private int age;
public Test06() {
}
public Test06(int age) {
super("小张");//使用super()方法完成对基类属性的初始化。
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void fun(){
String n = super.getName();//派生类可直接访问基类方法,也可使用super.的方式。
}
}
如果在构造方法中的第一行没有super()和this(),则会默认有一个super(无参数) 在第一行,以完成对基类属性的初始化。并且,可以推测得到,基类的构造方法总是会执行,且是最先执行完的。
此外,Java中允许派生类中出现和基类中同名的属性 ,它们可以同时存在。此时该属性的this.属性名和super.属性名所代表的内容就不再一样。
public class Test07{
public static void main(String[] args) {
Test09 t9 = new Test09("李四");
//当基类和派生类中有同名属性时,this和super所指就不一样了,此时若想访问基类的属性super就不可以省略
t9.test09Print();//派生类的name李四 基类的name李四
//因为派生类中没有为本类中的name设置setter和getter方法,所以修改和读取的都是基类的同名属性name。
t9.setName("王五");
t9.test09Print();//派生类的name李四 基类的name王五
}
}
class Test08 {
private String name;
public Test08() {
}
public Test08(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Test09 extends Test08{
private String name;
public Test09() {
}
public Test09(String name) {
super(name);
this.name = name;
}
public void test09Print(){
System.out.println("派生类的name"+this.name);
System.out.println("基类的name"+super.getName());
}
}
上面代码在堆中内存大致可由下图表示:
super与this不同,它不是引用,也不保存地址,仅代表当前基类的特征,不能直接输出,而this可以。
3. 方法覆盖(Override)
也叫方法重写,是指派生类在继承基类后,发现基类的方法无法满足需求而对基类的方法进行重写。
方法覆盖要满足的条件有四个:
1、要先继承,才有方法覆盖;
2、返回值类型相同或者是基类方法的子类,方法名和形式参数列表都要一致。
3、访问级别不能更低(四个访问控制权限的关系为 public > protected > 默认(也就是不写)> private
4、不能抛出更多的异常
class Test08 {
private String name;
public Test08() {
}
public Test08(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void doSome(){
System.out.println("基类的方法");
}
}
class Test09 extends Test08{
public Test09() {
}
public Test09(String name) {
super(name);
}
@Override//方法覆盖
public void doSome() {
System.out.println("派生类的方法");
}
}
注意:
方法覆盖仅针对方法,与属性无关;
构造方法和私有方法无法被覆盖;
覆盖主要针对实例方法,静态方法覆盖没有意义。
3.多态
多态指的是同一种行为具有不同表现形式的能力。Java中的多态表现为:基类型引用指向派生类型的对象。
多态一般要满足三个条件:
1、要先有继承
2、要进行方法覆盖
3、要向上转型
向上转型: 就是指用基类型引用指向派生类型的对象。向上转型是自动转换的。
格式:
基类 变量名 = new 派生类( );
向下转型:先有向上转型再有向下转型。指基类引用再转换为派生类。需要该基类引用创建时的类别与要转换的类别一致,如果转换的类不是该基类的派生类则编译不通过,如果是该基类的派生类,却和最初创建对象的派生类的类别不同,则在运行时报类型转换异常(ClassCastException)。向下转型是强制类型转换。
格式:
派生类 新变量名 = (派生类)基类型引用
public class Animal {
public Animal() {
}
public void move(){
System.out.println("移动");
}
public static void main(String[] args) {
//多态,基类型引用指向派生类型的对象
//向上转型,自动转换。
Animal cat = new Cat();
cat.move();//猫在跑
//要想调用派生类的特殊方法就得向下转型
//为了防止出现类型转换错误最好先判断一下,使用instanceof操作符
if(cat instanceof Cat){
Cat c = (Cat) cat;
c.doSome();//猫特有的行为
}
}
}
class Cat extends Animal{
public Cat() {
}
@Override//方法覆盖
public void move() {
System.out.println("猫在跑");
}
public void doSome(){
System.out.println("猫特有的行为");
}
}
class Bird extends Animal{
public Bird() {
}
@Override
public void move() {
System.out.println("鸟儿在飞翔");
}
}
多态会经过两个阶段:
编译阶段:会将基类型引用绑定基类的方法(静态绑定)
运行阶段:会将基类型引用绑定派生类型覆盖了的方法(动态绑定)
在调用派生类的特殊方法时,要向下转型,为了防止出现类型转换错误,需要现用instanceof操作符判定一下。它会在运行阶段动态判断引用绑定的对象类型,运算的结果是布尔型。
六、final关键字
final关键字可以修饰类,方法和变量。
修饰类时:该类无法被继承
修饰方法时:该方法无法被覆盖
修饰变量时:该变量只能被赋值一次,如果是局部变量,该变量被初始化后就不能被更改。如果是成员变量,系统就不会再赋默认值,所以必须在定义时赋值,或者在构造方法中赋值,一旦被赋值也就不能被修改。
//final修饰类时该类无法被继承
final class Test10{
private final int a;
private final String n;
//被public static final修饰的称为常量
public static final String d = "10086";
public Test10() {
//被final修饰的成员变量可在定义时直接赋值,也可以在构造方法中赋值
//在定义时和无参构造方法中赋值后,就不能再对值进行修改,否则报错
a = 100;
n = "helllo";
}
public Test10(int a, String n) {
this.a = a;
this.n = n;
}
//final修饰方法时该方法无法被覆盖
public final void fun(){
//final修饰局部变量后该局部变量只能被赋值一次
final int i = 1;
final Test09 tt1 = new Test09();
//tt1 = null 会报错,当引用被final修饰后保存的地址就不能被修改
final Test09 tt2 = null;
//tt2 = new Test09();也会报错
}
}
static final 联合修饰的一般成为常量,常量一般公开(public)
当final修饰引用时,引用保存的地址就不能再被修改,即使再赋null也不行。
如有错误,希望能批评指正,感谢。