Java基础——面向对象

Java基础——面向对象Java 字符串就是 Unicode 字符序列,例如字符串“Java”就是 4 个 Unicode 字符’J’、’a’、’v’、’a’组成的。

大家好,欢迎来到IT知识分享网。

一、面向过程和面向对象思想

面向过程和面向对象都是对软件分析、设计和开发的一种思想,它指导着人们以不同的方式去分析、设计和开发软件。早期先有面向过程思想,随着软件规模的扩大,问题复杂性的提高,面向过程的弊端越来越明显的显示出来,出现了面向对象思想并成为目前主流的方式。两者都贯穿于软件分析、设计和开发各个阶段,对应面向对象就分别称为面向对象分析(OOA)、面向对象设计(OOD)和面向对象编程(OOP)。C 语言是一种典型的面向过程语言,Java 是一种典型的面向对象语言。

面向过程思想思考问题时,我们首先思考“怎么按步骤实现?”并将步骤对应成方法,一步一步,最终完成。 这个适合简单任务,不需要过多协作的情况下。

面向过程适合简单、不需要协作的事务,重点关注如何执行。但是当我们思考比较复杂的设计任务时,此时面向对象思想就应运而生了。面向对象(Oriented-Object)思想更契合人的思维模式。我们首先思考的是“怎么设计这个事物?”

面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。 但是,具体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理。我们千万不要把面向过程和面向对象对立起来。他们是相辅相成的。面向对象离不开面向过程!

面向对象和面向过程思想的总结:都是解决问题的思维方式,都是代码组织的方式。面向过程是一种“执行者思维”,解决简单问题可以使用面向过程。面向对象是一种“设计者思维”,解决复杂、需要协作的问题可以使用面向对象。面向对象离不开面向过程:宏观上:通过面向对象进行整体设计;微观上:执行和处理数据,仍然是面向过程。

二、对象和类的详解

类:我们叫做 class。 对象:我们叫做 Object,instance(实例)。以后我们说某个类的对象,某个类的实例。是一样的意思。

总结:类可以看成一类对象的模板,对象可以看成该类的一个具体实例。类是用于描述同一类型的对象的一个抽象概念,类中定义了这一类对象所应具有的共同的属性、方法。

1.类的定义

// 每一个源文件必须有且只有一个public class,并且类名和文件名保持一致! public class Car { } class Tyre { // 一个Java文件可以同时定义多个class } class Engine { } class Seat { }

上面的类定义好后,没有任何的其他信息,就跟我们拿到一张张图纸,但是纸上没有任何信息,这是一个空类,没有任何实际意义。所以,我们需要定义类的具体信息。对于一个类来说,一般有三种常见的成员:属性 field、方法 method、构造器 constructor。这三种成员都可以定义零个或多个。

2.属性(field 成员变量)

属性用于定义该类或该类对象包含的数据或者说静态特征。属性作用范围是整个类体。在定义成员变量时可以对其初始化,如果不对其初始化,Java 使用默认的值对其初始化

成员变量的默认值

数据类型

默认值

整型

0

浮点型

0.0

字符型

‘\u0000’

布尔型

false

所有引用类型

null

属性定义的格式:

【修饰符】 属性类型 属性名=【默认值】;

3.方法

方法用于定义该类或该类实例的行为特征和功能实现。方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。

方法定义格式:

[修饰符] 方法返回值类型 方法名(形参列表) {

// n 条语句

}

方法的详细说明:

形式参数:在方法声明时用于接收外界传入的数据。

实参:调用方法时实际传给方法的数据。

返回值:方法在执行完毕后返还给调用它的环境的数据。

返回值类型:事先约定的返回值的数据类型,如无返回值,必须指定为 void。

注意事项:

1.实参的数目、数据类型和次序必须和所调用的方法声明的形式参数列表匹配。

2.return 语句终止方法的运行并指定要返回的数据。

3.Java 中进行方法调用中传递参数时,遵循值传递的原则(传递的都是数据的副本):基本类型传递的是该数据值的 copy 值。引用类型传递的是该对象引用的 copy 值,但指向的是同一个对象。

方法的重载(overload):

方法的重载是指一个类中可以定义多个方法名相同,但参数不同的方法。调用时,会根据不同的参数自动匹配对应的方法。重载的方法,实际是完全不同的方法,只是名称相同而已!

构成方法重载的条件

不同的含义:形参类型、形参个数、形参顺序不同。

只有返回值不同不构成方法的重载,如:int a(String str){}与 void a(String str){}不构成方法重载。

只有形参的名称不同,不构成方法的重载,如:int a(String str){}与 int a(String s){}不构成方法重载。

package cn.pxy.test; public class OverloadTest { public static void main(String[] args) { System.out.println(add(3,5)); System.out.println(add(3,5,10)); System.out.println(add(3.0,5)); System.out.println(add(5,3.0)); } //求和方法 public static int add(int n1,int n2) { int sum=n1+n2; return sum; } //方法名相同,参数个数不同构成重载 public static int add(int n1,int n2,int n3) { int sum=n1+n2+n3; return sum; } //方法名相同,参数类型不同构成重载 public static double add(double n1,int n2) { double sum=n1+n2; return sum; } //方法名相同,参数顺序不同,构成重载 public static double add(int n1,double n2) { double sum=n1+n2; return sum; } }

运行结果:

Java基础——面向对象

4.构造方法(构造器constructor)

构造方法基础用法:

构造器也叫构造方法(constructor),用于对象的初始化。构造器是一个创建对象时被自动调用的特殊方法,目的是对象的初始化。构造器的名称应与类的名称一致。Java 通过new 关键字来调用构造器,从而返回该类的实例,是一种特殊的方法。

声明格式:

[修饰符] 类名(形参列表){

//n 条语句

}

构造器 4 个要点:

1.构造器通过 new 关键字调用!2.构造器虽然有返回值,但是不能定义返回值类型(返回值的类型肯定是本类),不能在构造器里使用 return 返回某个值。3.如果我们没有定义构造器,则编译器会自动定义一个无参的构造方法。如果已定义则编译器不会自动添加!4.构造器的方法名必须和类名一致!

对象的创建完全是由构造方法实现的吗?

不完全是。构造方法是创建 Java 对象的重要途径,通过 new 关键字调用构造器时,构造器也确实返回了该类对象,但这个对象并不是完全由构造器负责创建的。创建一个对象分为如下四步:

1.分配对象空间,并将对象成员变量初始化为 0 或空

2.执行属性值的显式初始化

3.执行构造方法

4.返回对象的地址给相关的变量

构造方法的重载:

package cn.pxy.test; public class User{ int id; String name; String pwd; public User() { } public User(int id,String name) { this.id=id; this.name=name; } public User(int id,String name,String pwd) { this.id=id; this.name=name; this.pwd=pwd; } public static void main(String[] args) { User u1=new User(); User u2=new User(101,"李四"); User u3=new User(102,"张三",""); } }

如果方法构造中形参名与属性名相同时,需要使用 this 关键字区分属性与形参。如上例所示:this.id 表示属性 id;id 表示形参 id。

5.一个典型的学生类的定义与UML图

package cn.pxy.test; public class SxtStu { int id; String name; int age; Computer comp; void study() { System.out.println("我在使用我的"+comp.brand+"电脑学习!"); } SxtStu(){ } public static void main(String[] args) { SxtStu stu=new SxtStu(); stu.name="张三"; Computer comp=new Computer(); comp.brand="联想"; stu.comp=comp; stu.study(); } } class Computer{ String brand;//品牌 }

运行结果:

Java基础——面向对象

对应的UML图:

Java基础——面向对象

三、面向对象的内存分析

1.程序执行的内存分析过程

Java虚拟机内存模型

Java基础——面向对象

为了分析程序执行的内存,Java 虚拟机的内存可以简单的分为三个区域:虚拟机栈 stack、堆 heap、方法区 method area。

虚拟机栈(简称:栈)的特点如下:

1.栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)

2.JVM 为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)

3.栈属于线程私有,不能实现线程间的共享!

4.栈的存储特性是“先进后出,后进先出”

5.栈是由系统自动分配,速度快!栈是一个连续的内存空间!

堆的特点如下:

1.堆用于存储创建好的对象和数组(数组也是对象)

2.JVM 只有一个堆,被所有线程共享

3.堆是一个不连续的内存空间,分配灵活,速度慢!

方法区(又叫静态区,也是堆)特点如下:

1.方法区是 JAVA 虚拟机规范,可以有不同的实现。

i.JDK7 以前是“永久代”

ii.JDK7 部分去除“永久代”,静态变量、字符串常量池都挪到了堆内存中

iii.JDK8 是“元数据空间”和堆结合起来。

2.JVM 只有一个方法区,被所有线程共享!

3.方法区实际也是堆,只是用于存储类、常量相关的信息!

4.用来存放程序中永远是不变或唯一的内容。(类信息、静态变量、字符串常量等)

示例

创建Person类:

package cn.pxy.test; public class Person { String name; int age; public void show() { System.out.println("姓名:"+name+",年龄:"+age); } }

创建Person类对象并使用:

package cn.pxy.test; public class TestPerson { public static void main(String[] args) { //创建p1对象 Person p1=new Person(); p1.name="张三"; p1.age=18; p1.show(); //创建p2对象 Person p2=new Person(); p2.name="李四"; p2.age=22; p2.show(); } }

运行结果:

Java基础——面向对象

内存分配图:同一类的每个对象有不同的成员变量存储空间。同一类的每个对象共享该类的方法。

Java基础——面向对象

2.参数传值机制

Java 中,方法中所有参数都是“值传递”,也就是“传递的是值的副本”。 也就是说,我们得到的是“原参数的复印件,而不是原件”。因此,复印件改变不会影响原件。

基本数据类型参数的传值:传递的是值的副本。 副本改变不会影响原件。

引用类型参数的传值:传递的是值的副本。但是引用类型指的是“对象的地址”。因此,副本和原参数都指向了同一个“地址”,改变“副本指向地址对象的值,也意味着原参数指向对象的值也发生了改变”。

示例:多个变量指向同一个对象

package cn.pxy.test; public class User{ int id; String name; String pwd; public User(int id,String name) { this.id=id; this.name=name; } public static void main(String[] args) { User u1=new User(101,"李四"); User u3=u1; System.out.println(u1.name); u3.name="张三"; //引用类型参数传递会改变原先的值 System.out.println(u1.name); } }

运行结果:

Java基础——面向对象

四、this、static 关键字

1.this关键字

对象创建的过程和 this 的本质:

构造方法是创建 Java 对象的重要途径,通过 new 关键字调用构造器时,构造器也确实返回该类的对象,但这个对象并不是完全由构造器负责创建。创建一个对象分为如下四步:

1.分配对象空间,并将对象成员变量初始化为 0 或空

2.执行属性值的显式初始化

3.执行构造方法

4.返回对象的地址给相关的变量

this 的本质就是“创建好的对象的地址”! 由于在构造方法调用前,对象已经创建。因此,在构造方法中也可以使用 this 代表“当前对象”。

this 最常的用法:

在程序中产生二义性之处,应使用 this 来指明当前对象;普通方法中,this 总是指向调用该方法的对象。构造方法中,this 总是指向正要初始化的对象。

使用 this 关键字调用重载的构造方法,避免相同的初始化代码。但只能在构造方法中用,并且必须位于构造方法的第一句。

this 不能用于 static 方法中。

this关键字的使用

package cn.pxy.test; public class User { int id; //id String name; //账户名 String pwd; //密码 public User() { } public User(int id, String name) { System.out.println("正在初始化已经创建好的对象:"+this); this.id = id; //不写this,无法区分局部变量id和成员变量id this.name = name; } public void login(){ System.out.println(this.name+",要登录!"); //不写this效果一样 } public static void main(String[ ] args) { User u3 = new User(101,"张三"); System.out.println("打印张三对象:"+u3); u3.login(); } }

运行结果:

Java基础——面向对象

this()调用重载构造方法

package cn.pxy.test; public class TestThis { int a,b,c; TestThis(){ System.out.println("正要初始化一个Hello对象"); } TestThis(int a,int b){ //TestThis();//这样是无法调用构造方法的 this();//调用无参构造方法,并且必须位于第一行 a=a;//这里都是指的局部变量而不是成员变量 this.a=a;//这样就区分了局部变量和成员变量,这种情况占了this使用情况的大多数 this.b=b; System.out.println(a+b); } TestThis(int a,int b,int c){ this(a,b);//调用带参的构造方法,并且必须位于第一行 this.c=c; System.out.println(a+b+c); } void sing() { System.out.println("sing...."); } void eat() { this.sing();//调用本类的sing() System.out.println("回家吃饭!"); } public static void main(String[] args) { TestThis hi=new TestThis(2,3); hi.eat(); } }

运行结果:

Java基础——面向对象

2.static关键字

在类中,用 static 声明的成员变量为静态成员变量,也称为类变量。 类变量的生命周期和类相同,在整个应用程序执行期间都有效。它有如下特点:

为该类的公用变量,属于类,被该类的所有实例共享,在类被载入时被显式初始化。

对于该类的所有对象来说,static 成员变量只有一份。被该类的所有对象共享!!

一般用“类名.类属性/方法”来调用。(也可以通过对象引用或类名(不需要实例化)访问静态成员。)

在 static 方法中不可直接访问非 static 的成员。

static关键字的使用:

package cn.pxy.test; public class User { int id; //id String name; //账户名 String pwd; //密码 static String company="头条号";//公司名 public User(int id, String name) { this.id = id; //不写this,无法区分局部变量id和成员变量id this.name = name; } public void login(){ System.out.println(this.name+",要登录!"); //不写this效果一样 } public static void printCompany() { //login();//调用非静态成员,编译就会报错 System.out.println(company); } public static void main(String[ ] args) { User u = new User(101,"张三"); User.printCompany(); User.company="阿里巴巴"; User.printCompany(); } }

运行结果:

Java基础——面向对象

静态初始化块:

构造方法用于对象的初始化!静态初始化块,用于类的初始化操作!在静态初始化块中不能直接访问非 static 成员。

静态初始化块执行顺序:

上溯到 Object 类,先执行 Object 的静态初始化块,再向下执行子类的静态初始化块,直到类的静态初始化块为止。构造方法执行顺序和上面顺序一样

package cn.pxy.test; public class User { int id; //id String name; //账户名 String pwd; //密码 static String company;//公司名 static { System.out.println("这里执行类的初始化工作"); company="头条号"; printCompany(); } public static void printCompany() { System.out.println(company); } public static void main(String[ ] args) { User u = new User(); } }

运行结果:

Java基础——面向对象

五、包机制(package、import)

包机制是 Java 中管理类的重要手段。 开发中,我们会遇到大量同名的类,通过包我们很容易对解决类重名的问题,也可以实现对类的有效管理。 包对于类,相当于文件夹对于文件的作用。

1.package

我们通过 package 实现对类的管理,package 的使用有两个要点:

1.通常是类的第一句非注释性语句。

2.包名:域名倒着写即可,再加上模块名,便于内部管理类。

写项目时都要加包,不要使用默认包。com.pxy 和 com.pxy.car,这两个包没有包含关系,是两个完全独立的包。只是逻辑上看起来后者是前者的一部分。

JDK 中的主要包:

JDK 中的主要包

java中的常用包

说明

java.lang

包含一些 Java 语言的核心类,如 String、Math、Integer、System 和 Thread,提供常用功能。

java.awt

包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。

java.net

包含执行与网络相关的操作的类。

java.io

包含能提供多种输入/输出功能的类。

java.util

包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。

2.导入类import

如果我们要使用其他包的类,需要使用 import 导入,从而可以在本类中直接通过类名来调用,否则就需要书写类的完整包名和类名。import 后,便于编写代码,提高可维护性。

注意要点:

Java 会默认导入 java.lang 包下所有的类,因此这些类我们可以直接使用。

如果导入两个同名的类,只能用包名+类名来显示调用相关类:

java.util.Date date = new java.util.Date();

示例:导入同名类

import java.sql.Date; import java.util.*;//导入该包下所有的类。会降低编译速度,但不会降低运行速度。 public class Test{ public static void main(String[ ] args) { //这里指的是java.sql.Date Date now; //java.util.Date因为和java.sql.Date类同名,需要完整路径 java.util.Date now2 = new java.util.Date(); System.out.println(now2); //java.util包的非同名类不需要完整路径 Scanner input = new Scanner(System.in); } }

静态导入

静态导入(static import)是在 JDK1.5 新增加的功能,其作用是用于导入指定类的静态属性和静态方法,这样我们可以直接使用静态属性和静态方法。

package cn.pxy.test; //以下两种静态导入的方式二选一即可 import static java.lang.Math.*;//导入Math类的所有静态属性 import static java.lang.Math.PI;//导入Math类的PI属性 public class Test{ public static void main(String [ ] args){ System.out.println(PI); System.out.println(random()); } }

运行结果:

Java基础——面向对象

六、Object类详解

1.Object 类基本特性

Object 类是所有 Java 类的根基类,也就意味着所有的 Java 对象都拥有 Object 类的属性和方法。如果在类的声明中未使用 extends 关键字指明其父类,则默认继承 Object 类。

2.toString 方法

Object 类中定义有 public String toString()方法,其返回值是 String 类型。Object类中 toString 方法的源码为:

public String toString() {

return getClass().getName() + “@” + Integer.toHexString(hashCode());

}

根据如上源码得知,默认会返回“类名+@+16 进制的 hashcode”。在打印输出或者用字符串连接对象时,会自动调用该对象的 toString()方法。

示例:重写toString方法

package cn.pxy.test; class Person1 { String name; int age; @Override public String toString() { return name+",年龄:"+age; } } public class Test { public static void main(String[ ] args) { Person1 p=new Person1(); p.age=20; p.name="李四"; System.out.println("info:"+p); Test t = new Test(); System.out.println(t); } }

运行结果:

Java基础——面向对象

3.==和 equals 方法

“==”代表比较双方是否相同。如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。

Object 类中定义有:public boolean equals(Object obj)方法,提供定义“对象内容相等”的逻辑。比如,我们在学籍系统中认为学号相同的人就是同一个人。

Object 的 equals 方法默认就是比较两个对象的 hashcode,是同一个对象的引用时返回 true 否则返回 false。但是,我们可以根据我们自己的要求重写 equals 方法。

示例重写 equals()方法

package cn.pxy.test; public class TestEquals { public static void main(String[] args) { Person2 p1=new Person2(100,"张三"); Person2 p2=new Person2(100,"李四"); System.out.println(p1==p2);//false,不是同一个对象 System.out.println(p1.equals(p2));//true,id相同则认为两个对象内容相同 String s1=new String("胖咸鱼"); String s2=new String("胖咸鱼"); System.out.println(s1==s2);//false,两个字符串不是同一个对象 System.out.println(s1.equals(s2));//true,两个字符串内容相同 } } class Person2{ int id; String name; public Person2(int id,String name) { this.id=id; this.name=name; } public boolean equals(Object obj) { if(obj==null) { return false; }else { if(obj instanceof Person2) { Person2 c=(Person2)obj; if(c.id==this.id) { return true; } } } return false; } }

运行结果:

Java基础——面向对象

JDK 提供的一些类,如 String、Date、包装类等,重写了 Object 的 equals 方法,调用这些类的 equals 方法, x.equals (y),当 x 和 y 所引用的对象是同一类对象且属性内容相等时(并不一定是相同对象),返回true 否则返回 false。

4.super关键字

super“可以看做”是直接父类对象的引用。可以通过 super 来访问父类中被子类覆盖的方法或属性。使用 super 调用普通方法,语句没有位置限制,可以在子类中随便调用。

在一个类中,若是构造方法的第一行代码没有显式的调用 super(…)或者 this(…);那么Java 默认都会调用 super(),含义是调用父类的无参数构造方法。这里的 super()可以省略。

package cn.pxy.test; public class TestSuper { public static void main(String[] args) { new ChildClass().f(); } } class FatherClass{ public int value; public void f() { value=100; System.out.println("FatherClass.value="+value); } } class ChildClass extends FatherClass{ public int value; public void f() { super.f();//调用父类的普通方法 value=200; System.out.println("ChildClass.value="+value); System.out.println(value); System.out.println(super.value);//调用父类的成员变量 } }

super的使用运行结果:

Java基础——面向对象

七、继承

继承是面向对象编程的三大特征之一,它让我们更加容易实现对于已有类的扩展、更加容易实现对于现实世界的建模。继承有两个主要作用:1.代码复用,更加容易实现类的扩展2.方便建模

1.继承的实现

继承让我们更加容易实现类的扩展。 比如,我们定义了人类,再定义 Boy 类就只需要扩展人类即可。子类是父类的扩展。继承的使用:

package cn.pxy.test; public class Test{ public static void main(String[] args) { Student s=new Student("李四",18,"java"); s.rest(); s.study(); } } class Person1{ String name; int age; public void rest() { System.out.println("我要休息一会!"); } } class Student extends Person1{ String major; public void study() { System.out.println("我在学习英语!"); } public Student(String name,int age,String major) { //拥有父类的属性 this.name=name; this.age=age; this.major=major; } }

运行结果:

Java基础——面向对象

2.instanceof运算符

instanceof 是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对象时,返回 true;否则,返回 false。比如:在上例基础上测试:

public class Test{ public static void main(String[] args) { Student s=new Student("李四",18,"java"); System.out.println(s instanceof Person1); System.out.println(s instanceof Student); } } 两条输出语句都返回true。

3.继承使用的注意点

1.父类也称作超类、基类。子类:派生类等。

2.Java 中只有单继承,没有像 C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。

3.Java 中类没有多继承,接口有多继承。

4.子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。

5. 如果定义一个类时,没有调用 extends,则它的父类是:java.lang.Object。

4.方法的重写override

子类通过重写父类的方法,可以用自身的行为替换父类的行为。方法的重写是实现多态的必要条件。

方法的重写需要符合下面的三个要点:

1.“= =”:方法名、形参列表相同。

2.“≤”:返回值类型和声明异常类型,子类小于等于父类。

3.“≥”: 访问权限,子类大于等于父类

package cn.pxy.test; public class TestOverride { public static void main(String[] args) { Vehicle v1=new Vehicle(); Vehicle v2=new Plane(); v1.run(); v1.stop(); v2.run(); v2.stop(); } } class Vehicle{//交通工具类 public void run() { System.out.println("跑步。。。"); } public void stop() { System.out.println("停下来。。。"); } } class Plane extends Vehicle{ public void run() {//重写父类方法 System.out.println("天上飞~!!"); } public void stop() { System.out.println("停下来就坠机~!!"); } }

运行结果:

Java基础——面向对象

5.final关键字

final 关键字的作用:

修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。

final int MAX_SPEED = 120;

修饰方法:该方法不可被子类重写。但是可以被重载!

final void study(){}

修饰类:修饰的类不能被继承。比如:Math、String 等。

final class A {}

Java基础——面向对象

6.继承树追溯

属性/方法查找顺序:(比如:查找变量 h)

1.查找当前类中有没有属性 h。

2.依次上溯每个父类,查看每个父类中是否有 h,直到 Object。

3.如果没找到,则出现编译错误。

4.上面步骤,只要找到 h 变量,则这个过程终止。

构造方法调用顺序:

构造方法第一句总是:super(…)来调用父类对应的构造方法。所以,流程就是:先向上追溯到 Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。

注:静态初始化块调用顺序,与构造方法调用顺序一样,不再重复。

继承条件下构造方法执行过程:

package cn.pxy.test; public class TestSuper1 { public static void main(String[] args) { System.out.println("开始创建一个ChildClass对象。。。"); new ChildClass2(); } } class FatherClass2{ public FatherClass2() { System.out.println("创建FatherClass"); } } class ChildClass2 extends FatherClass2{ public ChildClass2() { System.out.println("创建ChildClass"); } }

运行结果:

Java基础——面向对象

7.继承和组合

我们可以通过继承方便的复用已经定义类的代码。还有一种方式,也可以方便的实现“代码复用”,那就是:“组合”。

“组合”不同于继承,更加灵活。“组合”的核心就是“将父类对象作为子类的属性”,然后,“子类通过调用这个属性来获得父类的属性和方法”。

package cn.pxy.test; public class Test{ public static void main(String[] args) { Student s=new Student("李四",18,"java"); s.person.rest(); s.study(); } } class Person1{ String name; int age; public void rest() { System.out.println("我要休息一会!"); } } class Student /*extends Person1*/{ Person1 person=new Person1(); String major; public void study() { System.out.println("我在学习英语!"); } public Student(String name,int age,String major) { //拥有父类的属性 this.person.name=name; this.person.age=age; this.person.rest(); this.major=major; } }

运行结果:

Java基础——面向对象

组合比较灵活。继承只能有一个父类,但是组合可以有多个属性。继承除了代码复用、也能方便我们对事物建模。所以,对于“is -a”关系建议使用继承,“has-a”关系建议使用组合。比如:上面的例子,Student is a Person 这个逻辑没问题,但是:Student has a Person就有问题了。这时候,显然继承关系比较合适。再比如:笔记本和芯片的关系显然是“has-a”关系,使用组合更好。

八、封装(encapsulation)

封装是面向对象三大特征之一。对于程序合理的封装让外部调用更加方便,更加利于写作。同时,对于实现者来说也更加容易修正和改版代码。

1.封装的作用和含义

封装就是把对象的属性和操作结合为一个独立的整体,并尽可能隐藏对象的内部实现细节。我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合是仅暴露少量的方法给外部使用,尽量方便外部调用。

就像我们要看电视,只需要按一下开关和换台就可以了,没有必要了解电视机内部的结构,制造厂家为了方便我们使用电视,把复杂的内部细节全部封装起来,只给我们暴露简单的接口,比如:电源开关。具体内部是怎么实现的,我们不需要操心。

编程中封装的具体优点:

提高代码的安全性。

提高代码的复用性。

“高内聚”:封装细节,便于修改内部代码,提高可维护性。

“低耦合”:简化外部调用,便于调用者使用,便于扩展和协作。

2.封装的实现—使用访问控制符

Java 是使用“访问控制符”来控制哪些细节需要封装,哪些细节需要暴露的。 Java中 4 种“访问控制符”分别为 private、default、protected、public,它们说明了面向对象的封装性,所以我们要利用它们尽可能的让访问权限降到最低,从而提高安全性。访问权限范围如表:

访问权限修饰符

修饰符

同一个类

同一个包中

子类

所有类

private

*

default

*

*

protected

*

*

*

public

*

*

*

*

1.private 表示私有,只有自己类能访问

2.default 表示没有修饰符修饰,只有同一个包的类能访问

3.protected 表示可以被同一个包的类以及其他包中的子类访问

4.public 表示可以被该项目的所有包中的所有类访问

3.封装的使用细节

开发中封装的简单规则:

属性一般使用 private 访问权限。

属性私有后, 提供相应的 get/set 方法来访问相关属性,这些方法通常是public 修饰的,以提供对属性的赋值与读取操作(注意:boolean 变量的 get方法是 is 开头!)。

方法:一些只用于本类的辅助性方法可以用 private 修饰,希望其他类调用的方法用 public 修饰。

封装的使用:

package cn.pxy.test; public class Test{ public static void main(String[] args) { Person1 p1=new Person1(); //p1.name="李四";//编译错误 p1.setName("李四"); p1.setAge(45); System.out.println(p1); Person1 p2=new Person1("张三",21); System.out.println(p2.getName()); } } class Person1{ private String name; private int age; public Person1() { } public Person1(String name,int age) { setName(name); setAge(age); } public void setName(String name) { this.name=name; } public String getName() { return name; } public void setAge(int age) { //在复制前先判断年龄是否合法 if(age>130||age<0) { this.age=18;//不合法赋值默认值18 }else { this.age=age; } } public int getAge() { return age; } public String toString() { return "Person1[name="+name+",age="+age+"]"; } }

运行结果:

Java基础——面向对象

九、多态(polymorphism)

1.多态概念和实现

多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。 比如:同样是调用人的“休息”方法,张三是睡觉,李四是旅游。

多态的要点

1.多态是方法的多态,不是属性的多态(多态与属性无关)。

2.多态的存在要有 3 个必要条件:继承,方法重写,父类引用指向子类对象。

3.父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。

package cn.pxy.test; class Animal{ public void shout() { System.out.println("叫了一声。"); } } class Dog extends Animal{ public void shout() { System.out.println("汪汪汪!"); } public void Work(){ System.out.println("看门!"); } } class Cat extends Animal{ public void shout() { System.out.println("喵喵喵!"); } } public class TestPolym { public static void main(String[] args) { Animal a1=new Cat();//向上可以自动类型转换 //传的具体是哪一类就调用那一个类的方法。大大提高了程序的可扩展性 animalCry(a1); Animal a2=new Dog(); animalCry(a2); /* * 编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换 * 否则通不过编译器的检查 */ Dog dog=(Dog)a2; dog.Work(); } //有了多态,只需要让增加的这个类继承Animal类就可以了 static void animalCry(Animal a) { a.shout(); } /** * 如果没有多态,这里需要写很多重载的方法 * 每增加一种动物,就需要重载一种动物的叫法,非常麻烦 * static void animalCry(Dog d){ * d.shout(); * } * static void animalCry(Cat c){ * c.shout; * } */ }

运行结果:

Java基础——面向对象

示例展示了多态最为多见的一种用法,即父类引用做方法的形参,实参可以是任意的子类对象,可以通过不同的子类对象实现不同的行为方式。

由此,可以看出多态的主要优势是提高了代码的可扩展性,符合开闭原则。但是多态也有弊端,就是无法调用子类特有的功能,比如,不能使用父类的引用变量调用 Dog类特有的 Work()方法。

2.对象的转型(casting)

父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型!

示例:对象的转型:

package cn.pxy.test; public class TestCasting { public static void main(String[] args) { Object obj=new String("胖咸鱼先生说");//向上可以自动转型 //obj.charAt(0);无法调用,编译器认为obj是Object类型而不是String类型 /* * 编写程序时,如果想调用运行时类型的方法,只能进行强制类型转换 */ String str=(String)obj;//向下转型 System.out.println(str.charAt(0));//位于0索引位置的字符 System.out.println(obj==str);//true,他们运行时是同一个对象 } }

运行结果:

Java基础——面向对象

示例:类型转换异常:

package cn.pxy.test; public class TestCasting { public static void main(String[] args) { Object obj=new String("胖咸鱼先生说"); //真实的子类类型是String,但是此处向下转型为StringBuffer StringBuffer str=(StringBuffer)obj; System.out.println(str.charAt(0)); } }

运行结果:

Java基础——面向对象

示例:向下转型中使用instanceof:

package cn.pxy.test; public class TestCasting { public static void main(String[] args) { Object obj=new String("胖咸鱼先生说"); if(obj instanceof String) { String str=(String)obj; System.out.println(str.charAt(0)); }else if(obj instanceof StringBuffer) { StringBuffer str=(StringBuffer)obj; System.out.println(str.charAt(0)); } } }

运行结果:

Java基础——面向对象

十、抽象类和接口

1.抽象方法和抽象类

抽象方法

使用 abstract 修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。

抽象类

包含抽象方法的类就是抽象类。通过 abstract 方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。

例如:

package cn.pxy.test; //抽象类 abstract class Animal1{ //抽象方法 abstract public void shout(); } class Pig extends Animal1{ //子类必须实现父类的抽象方法 public void shout() { System.out.println("哼哼哼"); } } public class TestAbstractClass { public static void main(String[] args) { Pig a=new Pig(); a.shout(); } }

抽象类的使用要点:

1.有抽象方法的类只能定义成抽象类

2.抽象类不能实例化,即不能用 new 来实例化抽象类。

3.抽象类可以包含属性、方法、构造方法。但是构造方法不能用来 new 实例,只能用来被子类调用。

4.抽象类只能用来被继承。

5.抽象方法必须被子类实现。

2.接口 interface

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是汽车,则必须能跑。

2.1接口的作用:

为什么需要接口?接口和抽象类的区别?

接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离

抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。

从接口的实现者角度看,接口定义了可以向外部提供的服务。从接口的调用者角度看,接口定义了实现者能提供那些服务。

接口是两个模块之间通信的标准,通信的规范。如果能把你要设计的模块之间的接口定义好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。接口和实现类不是父子关系,是实现规则的关系。即,普通类是具体实现;抽象类是具体实现、规范(抽象方法);接口是规范。

2.2如何定义和使用接口(JDK8 以前):

声明格式:

[访问修饰符] interface 接口名 [extends 父接口 1,父接口 2…]{

常量定义;

方法定义;

}

2.3定义接口的详细说明:

访问修饰符:只能是 public 或默认。

接口名:和类名采用相同命名机制。

extends:接口可以多继承。

常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。

方法:接口中的方法只能是:public abstract。 省略的话,也是 public abstract。

2.4要点

子类通过 implements 来实现接口中的规范。

接口不能创建实例,但是可用于声明引用变量类型。

一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是 public 的。

JDK1.8(不含 8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。JDK1.8(含 8)后,接口中包含普通的静态方法、默认方法。

2.5接口中定义静态方法和默认方法(JDK8 以后)

JAVA8 之前,接口里的方法要求全部是抽象方法。JAVA8(含 8)之后,以后允许在接口里定义默认方法和类方法。

默认方法:

Java 8 及以上新版本,允许给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做默认方法(也称为扩展方法)。默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供默认方法的实现,所有这个接口的实现类都会通过继承得到这个方法。

静态方法:

JAVA8 以后,我们也可以在接口中直接定义静态方法的实现。这个静态方法直接从属于接口(接口也是类,一种特殊的类),可以通过接口名调用。如果子类中定义了相同名字的静态方法,那就是完全不同的方法了,直接从属于子类。可以通过子类名直接调用。

2.6接口的多继承

接口完全支持多继承。和类的继承类似,子接口扩展某个父接口,将会获得父接口中所定义的一切。

interface A{ void testa(); } interface B{ void testb(); } /**接口可以多继承*/ interface C extends A,B{ void teatc(); } public class Test implements C{ public void testc(){ } public void testa(){ } public void testb(){ } }

2.7面向接口编程

面向接口编程是面向对象编程的一部分。接口就是规范,就是项目中最稳定的核心! 面向接口编程可以让我们把握住真正核心的东西,使实现复杂多变的需求成为可能。

通过面向接口编程,而不是面向实现类编程,可以大大降低程序模块间的耦合性,提高整个系统的可扩展性和和可维护性。面向接口编程的概念比接口本身的概念要大得多。设计阶段相对比较困难,在你没有写实现时就要想好接口,接口一变就乱套了,所以设计要比实现难!

十一、字符串String类详解

1.String基础

String 类又称作不可变字符序列。

String 位于 java.lang 包中,Java 程序默认导入 java.lang 包下的所有类。

Java 字符串就是 Unicode 字符序列,例如字符串“Java”就是 4 个 Unicode 字符’J’、’a’、’v’、’a’组成的。

Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是 String 类的一个实例。

Java 允许使用符号”+”把两个字符串连接起来。符号”+”把两个字符串按给定的顺序连接在一起,并且是完全按照给定的形式。当”+”运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接。

2.字符串相等判断

equals 方法用来检测两个字符串内容是否相等。如果字符串 s 和 t 内容相等,则s.equals(t)返回 true,否则返回 false。

要测试两个字符串除了大小写区别外是否是相等的,需要使用 equalsIgnoreCase方法。

判断字符串是否相等不要使用”==”。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/53929.html

(0)
上一篇 2024-08-02 20:15
下一篇 2024-08-03 10:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信