Java面向对象
一、面向对象概述
1、面向过程和面向对象
面向过程:考虑问题时,以一个具体的流程为单位,考虑它的实现办
法。关心的是功能的实现。强调的是功能行为
面向对象:考虑问题时,以具体的事物为单位,考虑它的属性(特征)
及动作(行为)。将功能封装进对象,强调的是具备了功能的对象
面向对象是基于面向过程的
2、面向对象的特点
•是一种符合人们思考习惯的思想
•可以将复杂的事情简单化
•将程序员从执行者转换成了指挥者
•完成需求时:
(1)先要去找具有所需的功能的对象来用。
(2)如果该对象不存在,那么创建一个具有所需功能的对象。
(3)这样简化开发并提高复用。
3、面向对象开发、设计、特征
•开发的过程:其实就是不断的创建对象,使用对象,指挥对象做事情。
•设计的过程:其实就是在管理和维护对象之间的关系。
•面向对象的特征:
(1)封装(encapsulation)
(2)继承(inheritance)
(3)多态(polymorphism)
二、类和对象的关系
类(class)和对象(object)是面向对象思想的核心概念
•类是对一类事物的描述,是抽象的、概念上的定义;
•对象(实体)是实际存在的该类事物的每个个体,因而也称实例(instance)。
对象是Java程序的核心,在Java程序中“万事万物皆对象”
•类描述了对象的属性和对象的行为,类是对象的模板,图纸。
•对象是类的一个实例,是一个实实在在的个体 。
•JDK提供了很多类供编程人员使用,编程人员也可以定义自己想要的类
三、封装类
1、封装
封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式。
好处: 将变化隔离。便于使用。提高重用性。提高安全性。
封装原则:
将不需要对外提供的内容都隐藏起来。
把属性都隐藏,提供公共方法对其访问。
抽象:
将客观存在的事物特征用Java语言描述出来。抽象只关注一个
2、类的定义
生活中描述事物无非就是描述事物的属性和行为。
如:人有身高,体重等属性,有说话,打球等行为。
Java中用类class来描述事物也是如此,java类包括:
•属性:对应类中的成员变量。
•行为:对应类中的成员方法。
**注:**定义类其实在定义类中的成员(成员变量和成员方法)。
加粗样式语法:
[修饰符] class 类名 {
//这里放属性和方法的声明
}
示例:封装一个汽车类
创建汽车类以及使用汽车类:
class Car {
//car属性
String color = "red";
int num = 4;
//行为方法
void show(){
System.out.println("color="+color+"..num="+num);
}
}
class CarTest {
public static void main(String[] args) {
Car c = new Car(); //创建对象
c.color = “black”; //操作对象的属性
c.show(); //使用对象的功能。
}
}
(1)类中成员变量的定义
定义成员变量的语法:
[访问修饰符] 数据类型 成员变量名 [= 初始值];
//定义成员变量
public String color = "red";
private int num = 4;
protected char name = "张三";
•成员变量的类型可以使用Java语言中的任 何一种数据类型(包括基本类型和引用类型)。
•在定义成员变量时可以对其进行初始化。如果不对其初始化,Java会使用默认的值对 其进行初始化。
•成员变量的作用域是整个类体。
基本数据类型默认值如下图所示:
成员变量和局部变量
•成员变量:
成员变量定义在类中,在整个类中都可以被访问。
成员变量随着对象的建立而建立,存在于对象所在的堆内存中。
成员变量有默认初始化值。
•局部变量:
局部变量只定义在局部范围内,如:方法内,语句内等。
局部变量存在于栈内存中。
作用的范围结束,变量空间会自动释放。
(2)类中成员方法的定义
•定义方法的语法:
[修饰符] 返回值类型 方法名 (参数类型 参数名1,…) {
// 这里放方法的具体实现语句
}
构造方法:
•构造方法的作用在于构造并初始化对象。
•利用new关键词调用类的构造方法(类的一种特殊方法)就可创建该类的一个对象。
•语法要求
构造方法的名字和类名相同,并且没有返回值,也不要加void。
两种构造方法
•参数花构造方法
•默认构造方法
默认构造方法就是指不带参数的构造方法。
•Java的类都要求有构造方法,如果没有定义构造方法,Java编译器会
为我们提供一个默认的构造方法。
•一个类多个构造方法:
如果类中有一个自己编写的构造方法时,编译器就不会为我们再提供
那个默认构造方法。此时又希望还可以用默认构造方法来创建类的实
例时,那就必须在类中明确添加这个默认构造方法。
3、内存分析
封装一个学生(Student)类:
•学生应该有姓名(name)、年龄(age)、性别(gender),班级号(classNum),座
位号(sno)。
•提供一个参数化构造化方法以便能很方便的创建一个学生对象。
•提供一个方法(displayInfo())用来输出显示这个学生的姓名、年龄、性别、所在的班级和他的座位号。
•写一个main方法创建两个学生对象,分别调用displayInfo()方法显示各自的信 息。
四、方法重载
方法重载:
指的是同一个类中可以定义有相同名字,但参数列表不同的
多个方法。调用时,会根据不同的参数选择对应的方法。
•参数列表是指参数的类型,个数或顺序
•类中定义的普通方法、构造方法都可以重载
•重载的特点:
与返回值类型无关,只看参数列表。
•作用:
•方便于使用者,优化了程序设计
示例:
class person{
private String name; //姓名
private boolean sex; //性别
private int age; //年龄
//有参构造
public Person(String n, boolean s, int a){
name = n;
sex = s;
age = a;
}
//无参构造
public Person(){}
public void speak(String word){
//说话
System.out.println(name + “说:” + word);
}
//重载speak方法
public void speak(){
System.out.println(“无语…”);
}
public static void main(String [] args){
Person person = new Person();
//根据参数列表的不同调用不同的方法
person.speak(“你好”);
person.speak();
}
}
五、This关键
•每个类的每个非静态方法(没有被static修饰)都会隐含一个this引用名
称,它指向调用这个方法的对象
•当在方法中使用本类的属性时,都会隐含地使用this名称,当然也可
以明确指定。
•this可以看作是一个变量,它的值就是当前对象的引用
This用法一
•当类中某个非静态方法的参数名跟类的某个成员变量名相同时,为了
避免参数的作用范围覆盖了成员变量的作用范围,必须显式地使用this关键字来指定成员变量
public class Employee {
private String name; //姓名
private int age; //年龄
private double salary; //薪水
public Employee(String name, int age, double salary){ //构造方法
this.name = name;
this.age = age;
this.salary = salary;
}
}
This用法二
public class Employee {
private String name; //姓名
private int age; //年龄
private double salary; //薪水
public Employee(String name, int age, double salary){ //构造方法1
this.name = name;
this.age = age;
this.salary = salary;
}
public Employee(){
//构造方法2
this(“无名”, 18, 800.0); //使用this调用到了构造方法1
}
}
六、包
•为了便于管理大型软件系统中数目众多的类,解决类命名冲突的问题,
Java引入了包。
•用package来声明包,package语句必须是java源文件中的第一条语
句。(若无这条语句,则放置在无名包下)
•在package语句中,用"."来指明包(目录)的层次。包对应着文件系
统的目录层次结构。
•如:package com.liqk; 编译后对应的类文件位于com\liqk目录下。
创建包:
编译和运行带包的类:
•在当前目录下生成带包结构的字节码
cmd>javac –d . Employee.java
•在指定目录下生成带包结构的字节码
cmd>javac –d D:\share Employee.java
•运行带包的类:
cmd>java 包名.类名
导入包:
•导入某个包中的所有类使用:包名.*
如:import com.qiujy.*;
•同一包中的类之间直接引用,无需import语句
•无名包中的类,无法在其它带包的类中使用。
•建议:自定义类都要放置在包中。
Jdk中主要包的介绍
•java.lang -包含一些Java语言的核心类,如:Object、String、Math、Integer、System和Thread,提供常用功能。此包非常常用,所以在任何类中不用导入就可能直接使用。
•java.util -包含一些实用工具类,如定义系统特性、日期时间、日历、集合类等。
•java.io -包含能提供多种输入输出的流类。
•java.net -包含执行网络相关的操作的类。
•java.sql -java操作数据库的一些API。
•java.text-包含一些用来处理文本、日期、数字和消息的类和接口
•java.awt -包含了构成抽象窗口工具集的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
七、类的继承
•继承关系:两个类之间是“is-a”(是一个)的关系。
继承的特点:
•子类继承了父类(父类,基类)的所有属性和方法(但不包括构造器),并可以为自己增添新的属性和方法。
•继承可提高代码的重用性,使用extends关键字来实现。
•Java只支持单继承:即一个子类只能有一个父类。但一个父类可以派生出多个子类。
示例:
class Employee {
private String name; //姓名
private double salary = 2000.0; //薪水
public Employee(String name, double salary){
this.name = name;
this.salary = salary;
}
public Employee(){}
public double getSalary(){
return salary;
}
}
class Manager extends Employee{ //继承自Employee类
private double bonus; //奖金
public void setBonus(double bonus){
this.bonus = bonus;
}
}
public class InheritanceTest {
public static void main(String [] args){
Manager manager = new Manager();
manager.getSalary();
}
}
子类的实例化过程
调用子类构造方法创建一个对象时:
•会先调用父类构造方法对父类中的属性迚行初始化,然后才调用自己的构造方法 对自己的属性来迚行初始化
•如果子类构造方法没有显式调用父类构造方法,那么会调用父类的默认构造方法。
•如果父类没有默认构造方法,而子类构造方法又没有显式调用父类的其它构造 方法,那么编译将报错
八、Super关键字
Super的第一种用法
要在子类构造方法中显式调用父类的构造方法,则在子类构造方法的第一条语句用关键字 super来调用。
•语法为:
super() ; //显式调用父类的无参构造方法
super(实参列表) ; //显式调用父类的带参构造方法
示例:
class Employee {
private String name; //姓名
private double salary = 2000.0; //薪水
public Employee(String name, double salary){
this.name = name;
this.salary = salary;
}
public Employee(){}
public double getSalary(){ return salary; }
public void displayInfo(){ System.out.println(“name=” + name); }
}
class Manager extends Employee{
private double bonus; //奖金
private String position; //职位
public Manager(String name, double salary, String position){
super(name, salary);//使用super调用父类构造函数
this.position = position;
}
public void setBonus(double bonus){ this.bonus = bonus; }
}
super的第二种用法
super还可用来显式调用父类的普通方法。
语法:
super.方法名(实参列表);
示例:
class Employee {
…
public void displayInfo(){ System.out.println(“name=” + name); }
}
class Manager extends Employee{
private double bonus; //奖金
private String position; //职位
public Manager(String name, double salary, String position){
super(name, salary);//super调用父类构造函数
this.position = position;
super.displayInfo(); //super调用父类普通方法
}
public void setBonus(double bonus){ this.bonus = bonus; }
}
九、访问控制
•访问修饰符可以修饰类、成员变量、方法
•private: 本类可见 (经常用来修饰类中的属性)
**•默认:**本类、本包可见 (不建议使用)
•protected:对本类、所有子类和本包可见 (用法特殊)
**•public:**对一切可见 (经常用来修饰类,修饰类中的方法)
•对于外部类的访问修饰,只可以是public或 默认 的。
示例:
class T {
private int i = 10;
int j = 100;
protected int k = 1000;
public int m = 100000;
}
public class AccessTest {
public static void main(String [] args) {
T t = new T();
System.out.println(t.i); //报错,因为变量i不能在其他类中使用
System.out.println(t.j);
System.out.println(t.k);
System.out.println(t.m);
}
}
十、方法重写
方法重写: 在子类中可以根据需要对从父类中继承来的方法迚行重写。
•重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型
•注意:
•子类中的重写方法的访问范围不能被缩小
•静态方法只能重写父类的静态方法。
示例:
class Employee {
private String name; //姓名
private double salary = 2000.0; //薪水
public Employee(String name, double salary){
this.name = name;
this.salary = salary;
}
public double getSalary(){ return salary; }
}
class Manager extends Employee{
private double bonus; //奖金
private String position; //职位
public Manager(String name, double salary, String position){
super(name, salary);
this.position = position;
}
public void setBonus(double bonus){ this.bonus = bonus; }
public double getSalary(){ //重写父类的getSalary()方法
return super.getSalary() + bonus;
}
}
十一、Static关键字
static声明的成员变量为静态成员变量
•它是该类的公用变量,在第一次使用时被初始化,对于该类的所有对象来说,static成员变量只有一份。也说成“类变量”。
static声明的方法为静态方法
•该方法独立于类的实例,所以也叫“类方法”。
•静态方法中只能调用本类中其它的静态成员(变量和方法)。
•静态方法中丌能使用this和super关键字。
•静态成员可以通过类名(不需要实例化)或类的实例去访问
示例:
静态代码快
•在类中可以使用不包含在仸何方法中的静态代码块(static block),当
类被JVM载入时,静态代码块被执行,且只被执行一次。
•静态代码块经常用来初始化类的静态成员变量。
Class Student{
private static String country;
private String name;
//使用static初始化静态成员
static {
country = "中国";
System.out.println("Student类已经加载");
}
public Student(String name){
this.name = name;
}
public void displayInfo(){
System.out.println("我的名字是:" + name + ",国家是:" + country);
}
public static void main(String[] args){
//Student stu = new Student("张三");
//stu.displayInfo();
}
}
十二、Final关键字
final修饰的变量(成员变量或局部变量)的值不能改变,即等同于常量,在定义时就必须要进行初始化,之后就再也不改变它的值了。
•final修饰方法的参数叫最终参数。调用这个方法时给最终参数赋值之后,在方法体内再也不能改变它的值。
•final修饰的方法不能被子类重写。
•final修饰的类不能被继承
示例:
public class TestFinal {
public static void main(String[] args) {
T t = new T();
//t.i = 100;//错误,final修饰的变量不能被修改
}
}
final class T {
final int i = 10;
public final void m() { //j = 11; }
}
class TT extends T {}//错误,final修饰的类不能被继承
十三、多态
多态定义:同一类事物的多种存在形态
1、对象多态性:
•一个父类变量可以“指向”其子类的对象。
•一个父类变量不可以访问其子类对象新增加的成员。
•可以使用instanceof关键字来判断该对象变量所“指向”的对象是否属于该类。
•语法:
对象变量名 instanceof 类名(或接口名)
•子类的对象可以直接当作父类的对象使用,称作向上转型。它是自动进行的。
•从父类的对象到子类的对象的转换叫向下转型,向下转型要用强制类 型转换。
示例代码:
Class Animal{
private String name;
Animal(String name) {
this.name = name;
}
public String getName(){
return name;
}
}
class Cat extends Animal {
private String eyesColor;
Cat(String n,String c) {
super(n); eyesColor = c;
}
public String getEyesColor(){
return eyesColor;
}
}
class Dog extends Animal {
private String furColor;
Dog(String n,String c) {
super(n);
furColor = c;
}
public String getFurColor() {
return furColor;
}
}
public class CastingTest{
public static void main(String args[]){
Animal a = new Animal("动物");
Cat c = new Cat("猫","black");
Dog d = new Dog("狗","yellow");
System.out.println(a instanceof Animal);
System.out.println(c instanceof Animal);
System.out.println(d instanceof Animal);
System.out.println(a instanceof Cat);
//向上转型
Animal an = new Dog("旺财","yellow");
System.out.println(an.getName());
//error!父类不能调用子类新的方法
System.out.println(an.getFurColor());
System.out.println(an instanceof Animal); //true
System.out.println(an instanceof Dog); //true
//向下转型,要加强制转换符
//-- 为了安全起见,要加 instanceof 判断
Dog d2 = (Dog)an;
//Cat c2 = (Cat)an;
System.out.println(d2.getFurColor());
}
}
多态方法
•多态方法可以提高代码的可维护性和可扩展性。
发生多态的条件
(1)要有继承
(2)要有重写
(3)要有向上转型(父类变量指向子类对象)
(4)父类变量调用被子类重写的方法
示例:
class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public void enjoy(){
System.out.println("叫声......");
}
}
class Cat extends Animal {
private String eyesColor;
public Cat(String n) {super(n);}
public void enjoy(){
System.out.println("喵~喵~");
}
}
class Dog extends Animal {
private String furColor;
public Dog(String n) {super(n);}
public void enjoy(){
System.out.println("汪~汪~");
}
}
class Lady{
private String name;
private Animal pet;
public Lady(String name, Animal pet) {
this.name = name;
this.pet = pet;
}
public void myPetEnjoy(){
pet.enjoy();
}
}
public class DynamicBindingTest {
public static void main(String args[]){
Cat c = new Cat("catname","blue");
Lady l = new Lady("张女士", c);
l.myPetEnjoy();
}
}
十四、抽象类
•用abstract修饰的方法叫抽象方法。
•抽象方法形式:(没有方法体)
[访问修饰符] abstract 返回类型 方法名(参数列表);
•用abstract修饰的类叫抽象类。
•声明抽象类语法:
•[访问修饰符] abstract class 类名{…… }
(1)抽象类不能被实例化。
(2)含有抽象方法的类必须被声明抽象类。
(3)抽象类的子类必须重写所有的抽象方法后才能被实例化,否则这个子类必须被定义为抽象类
示例:
abstract class Shape { //形状类
protected double length; //长
protected double width; //宽
public Shape(double length, double width){
this.length = length; this.width = width;
}
public abstract double area(); //计算面积抽象方法
}
class Rectangle extends Shape{ //矩形
Rectangle(final double num, final double num1) { super(num, num1); }
public double area() { return length * width; }//重写抽象方法
}
class Triangle extends Shape{ //三角形
Triangle(final double num, final double num1) { super(num, num1); }
public double area() { return length * width/2; }//重写抽象方法
}
public class TestAbstract{
public static void main(String[] args){ … }
}
十五、接口
•接口就是某个事物对外提供的一些功能的声明。
•可以利用接口实现多态,同时也弥补Java单一继承的弱点
•使用interface关键字定义接口。
•一般使用接口声明方法或静态最终变量(常量),接口中的方法只能是声明,不能是具体的实现。
•接口中的仸何方法都自动置为public的,属性也总是public static
final的。
•接口没有构造方法,所以不能被实例化(不能用来创建对象)。
•从JavaSE 8开始,接口中也可以声明默认方法不静态方法。默认方法
使用default修饰,可以通过实现类的对象调用。静态方法使用static修饰,可以通过接口名调用
创建接口示例:
//方法接口
public interface Runner{
public void run();
}
//定义常量的接口
public interface Constants{
public static final int COLOR_RED = 1;
public static final int COLOR_GREEN = 2;
public static final int COLOR_BLUE = 3;
}
实现接口:
用关键字 implements 实现接口。如:
class Car implements Runner
•每个类只能有一个父类,但可以实现多个接口。如果实现多个接口,则用逗号隔开接口名称,如下所示:
•class Car implements Runner, Constants
•一个类实现了一个接口,它必须实现接口中定义的所有方法,否则该 类必须声明为抽象类。
•接口可以继承自其它的接口,并添加新的常量和方法。接口支持多重 继承。
示例:
class Car implements Runner,Constants{ //实现两个接口
public void run(){
System.out.println("车颜色是:" + COLOR_RED);
System.out.println("用四个轮子跑...");
}
}
interface Animal extends Runner{ //接口的继承
void breathe(); //呼吸
}
class Fish implements Animal{
public void run(){
System.out.println("颜色是:" + COLOR_BLUE);
System.out.println(“游啊游...");
}
public void breathe(){
System.out.println("冒气泡来呼吸");
}
}
十六、嵌套类
•声明在类的内部的类称之为嵌套类(nested class)
•定义语法
[public] class OuterClass{
...
[public|proteceted|private] [static] class NestedClass{
...
}
}
分类
•内部类:非静态嵌套类
•静态嵌套类:用static修饰的嵌套类
•嵌套类在编译后会生成OuterClass$NestedClass.class类文件
内部类
•内部类作为外部类的一个成员存在,与外部类的成员变量、成员方法并列。
示例
class Outer {
private int outer_i = 100;
private int j = 123;
public void test() { System.out.println("Outer:test()"); }
public void accessInner(){
Inner inner = new Inner();//外部类中使用内部类也需要创建出对象
inner.display();
}
public class Inner{//内部类
private int inner_i = 100;
private int j = 789; //与外部类某个属性同名
public void display() {
//内部类中可直接访问外部类的属性
System.out.println("Inner:outer_i=" + outer_i);
test(); //内部类中可直接访问外部类的方法
//内部类可以用this来访问自己的成员
System.out.println("Inner:inner_i=" + this.inner_i);
System.out.println(j); //访问的是内部类的同名成员
//通过“外部类.this.成员名”来访问外部类的同名成员
System.out.println(Outer.this.j);
}
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
outer.accessInner();
//在外部类以外的地方创建内部类的对象
Outer.Inner inner = outer.new Inner();
inner.display();
}
}
静态嵌套类
静态嵌套类中可声明static成员或非静态成员,但只能访问外部类中的静态成员。
[public ] class OuterClass{
...
static class StaticNestedClass { //静态嵌套类
...
}
}
方法类
方法类(局部内部类):在方法体内声明的类
•可访问它所在方法中的final参数和final局部变量
•由于线程不并发的原因,局部内部类仅能方法中的final参数和final局部变量
public class Outer{
public void test(){
final int x = 9;//可以访问final修饰的变量
int y = 10;//jdk8.0后 可以访问只赋值过一次的变量
//y = 12;//非final修饰的变量,如果改变它的值,则不能被方法类问
class FunctionInner{//方法类(局部内部类)
public void show(){
System.out.println(x+" "+y);
}
}
FunctionInner f = new FunctionInner();//创建方法类对象
f.show();//调用方法类中的方法
}
}
说明:从JavaSE 8开始,方法类也能访问非final的局部变量,但前提是该局部变量只能被赋值一次,因此从效果上说,该变量不final的等价的。
匿名内部类
匿名内部类:没有声明名称的内部类
匿名类与方法类相同,仅能访问方法中final的局部变量。JavaSE 8以 后也可以访问final等价的局部变量。
[public ] class OuterClass{
...
new 已经存在的类名或接口名 () {
...
}
}
//------------------------------------------------
public class AnnonymoseInnerClassTest {
public static void main(String[] args) {
(new AClass("redhacker") {
public void print() { //对父类的print方法进行覆盖
System.out.println("the anonymose class print");
super.print(); //调用父类中的print方法
}
}).print(); //调用覆盖后的print方法
}
}
class AClass {
private String name;
AClass(String name) { this.name = name;}
public void print() {
System.out.println("SuperClass:The name = " + name);
}
}
说明
•方法类和匿名内部类最常见的用处就是实现一个接口,而这个接口通
常都只会使用一次,所以不用专门用一个类去实现它。
•比起方法类,匿名类更加简洁。但当类体较长时,这时候就不适合使
用匿名类了,而是应该使用方法类。