java从零开始系列——(二)面向对象 上

luoyjx · 2014-08-25 22:39 · 1719次阅读

面向对象?面向,对象?

是Java的面向对象~

                     

进入正题-------------------------------------------------------------------------------------------

概要

    1.源文件布局 
    2.类和对象  
    3.方法 
    4.构造器 
    5.变量 
    6.类的继承  
    7.访问修饰符  
    8.封装性 

源文件布局 

一个Java源文件可包含三个“顶级”要素: 
 (1)一个包(package)声明(可选) 
 (2)任意数量的导入(import)语句 
 (3)类(class)声明 
如图2-4

包的声明

多数软件系统是庞大的。为了方便管理,通常要将类组织成包。在包中可以存放类,也可以存放子包,从而形成具有层次结构的包。包可以根据需要任意组织,通常,要按照类的用途、含义来组织包。如下UML 包图:
如图2-5

Java技术提供了包的机制,以此来组织相关的类。声明包的句法如下:
package <top_pkg_name> [.<sub_pkg_name>];
我们可以使用package命令指明源文件中的类属于某个特定的包。

包与目录

包保存在目录结构中,包的名字就是目录的名字。例如,com.test包中的PersonApp.class文件应该在 $path\com\test  目录中。

包的导入

当你想要使用包中的类的时候,可以用import命令告诉编译器类在哪里。import命令的语法:
import  包名称.子包名称.类名称;//手工导入所需要的类 
import  包名称.子包名称.;//由 JVM自动加载所需要的类
例如:
import com.test.
;
import java.util.List;
import java.io.*;

静态导入为Java 的新特性 在JDK 1.5 之后提供了静态导入功能。如果一个类中的方法全部是使用 static 声明的静态方法,则在导入的时候就可以直接使用“import static”的方式导入,导入的格式如下:import static  包.类.*; 
如果按照非静态的import 导入的话,则调用的时候肯定使用的是类.方法()。而使用了静态导入 import static  包.类.*的语句,则在主方法中直接使用该类中的方法即可。 

javac、 java、 jar、classpath 

编译JAVA带包名的源代码编译:
javac -d . HelloWorld.java
执行带包名的JAVA应用程序:
java com.test.HelloWorld
指定classpath的执行方式
java -classpath . com.test.HelloWorld

CLASSPATH环境变量作用:
当我们使用java java类名 命名来运行java程序时,JRE到哪里搜索java类?在classpath指定的路径。
Jdk1.4及之前版本,需要在CLASSPATH环境变量里面添加一点(.)。用来告诉jre需要在当前路径下面搜索java类,另外编译和运行还需要JDK的lib路径的dt.jar、tools.jar文件的java类。
在jdk1.5及以上版本完全不要设置CLASSPATH,sun公司改进jdk设计,jre会自动搜索当前路径下面的类文件,而且使用java的编译和运行工具时,系统可以自动加载dt.jar和tools.jar文件中的java类。在jdk1.5及以上版本设置CLASSPATH环境变量,jre就会按指定的路径搜索Java类。如果CLASSPATH没有包含一点(.),那么jre就不会在当前路径搜索java类。如果我们希望运行java程序时,临时指定jre搜索java类的路径,可以使用-classpath选项,格式如下:
java –classpath dir1;dir2;…;dirN java类
我们还可以想CLASSPATH环境指定的搜索路径有效,而且还会在当前路径搜索java类,格式如下
java –classpath %CLASSPATH%;.;dir1;dir2;…;dirN java类

Jar命令

JAR是一种与平台无关的文档格式,全称为Java Archive,翻译成中文叫Java 归档,它相当于一种压缩格式,可以把众多的文档(Java Applet及Application的类文件、图像或声音等格式)合成一个文件,就象ZIP,RAR等。
JAR文件好处
安全:能够对JAR文件进行数字签名,只让能够识别数字签名的用户使用里面的东西。
压缩:加快读取速度。文件变小,JAR的压缩机制跟ZIP完全相同
包封装:能够让JAR包里面的文件依赖于同一版本的类文件
可移植性:JAR包作为内嵌在java平台内部处理的标准,能够在各种平台上直接使用。

jdk提供了jar的工具,就在java所在目录的bin目录下有一个jar.exe的应用程序 ,可以使用此工具打包。
用法:jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 …
-c  创建新的归档
-t  列出归档内容的列表
-x  展开归档中的命名的(或所有的〕文件
-u  更新已存在的归档
-v  生成详细输出到标准输出上
-f  指定归档文件名
-m  包含来自指定的清单(manifest〕文件的清单(manifest〕信息
-0  只存储方式;未用ZIP压缩格式
-M  不产生所有项的清单(manifest〕文件
-i  为指定的jar文件产生索引信息
-C  改变到指定的目录,并且包含下列文件

类和对象

面向对象的核心组成部分:类与对象。 
类是对某一类事物的描述,是抽象的、概念上的定义; 
对象是实际存在的该类事物的每个个体,因而也称实例(instance)。 

定义类

JAVA中的语法和C一样,语句都是以分号结束。大小写区分。在Java技术中采用下列方法声明类:
    <修饰符>  class  <类名> {
           <零个或者多个属性定义>
           <零个或者多个构造器定义>
           <零个或者多个方法定义>
  }

如果一个类声明为 public class 则文件名称必须与类名称一致,而且在一个类中只能有一个 public class。而使用 class声明一个类,则文件名称可以与类名称不一致,但是执行的时候必须执行生成的 class文件名称。除了这些之外,public class 和class 还在包的访问上有所限制。如果一个类只在本包中访问,不需要被外包访问,则直接声明成 class即可,而如果一个类需要被外包访问,则必须声明为 public class。 一般在开发中对于一个*.java 文件中往往都只定义一个类:public class。

对象创建使用 

对象通过关键字new来创建
定义一个Person类
通过new关键字创建Person实例对象,并使用一个变量来指向。
语法:类名称 对象名称=new 类名称()
例如:
Person p=new Person();

对象内存分配

图2-6

关于这块不多解释,具体的可以参考我的其他两篇博文

关于java的运行时数据区域划分(上)

关于java的运行时数据区域划分(下)

匿名对象

匿名:没有名字,在Java 中如果一个对象只使用一次,则就可以将其定义成匿名对象。 
所谓的匿名对象就是比之前的对象少了一个栈内存的引用关系。
class Person{ 
  private String name ; 
  private int age ; 
  public Person(String n,int a){    //  声明构造方法,为类中的属性初始化 
    this.setName(n) ; 
    this.setAge(a) ; 
  } 
  public void setName(String n){ 
    name = n ; 
  } 
  public void setAge(int a){ 
    if(a>0&&a<150){ 
      age = a ; 
    } 
  } 
  public String getName(){ 
    return name ; 
  } 
  public int getAge(){ 
    return age ; 
  } 
  public void tell(){ 
    System.out.println(“姓名:” + this.getName() + “;年龄:” + this.getAge()) ; 
  } 
}

public class NonameDemo01{ 
  public static void main(String args[]){ 
    new Person(“张三”,30).tell() ; 
  } 
}

属性声明

语法:
<修饰符> <数据类型> <属性名称> [ =<属性默认值> ];

结果:
private double weight; 

方法概述

现实世界里由类和对象组成,方法只能作为类和对象的附属,java语言里的方法也是一样。
方法不能独立定义,方法只能在类里面定义。
方法要么属于一个类,要么属于一个对象。
方法不能独立执行,执行方法必须使用类或者对象作为调用者。

方法分类 

方法分为两种:
普通方法(对象的方法):没有static关键字修饰的方法 
静态方法(类的方法):使用static关键字修饰的方法 

方法定义

方法定义语法:
<修饰符>  <返回值>  <方法名称>  ( <(零个或者多个参数)参数类型 参数变量> ) {
<方法代码>
注:如果返回值为非void,使用return返回相应的值
}
方法名称规则:第一个单词的字母小写,之后每个单词的首字母大写。 
方法定义示例:
图2-7

方法使用

普通方法(对象的方法):
通过类的实例化对象来调用,如:
静态方法(类的方法):
通过类来调用

方法的参数传递机制

 
参数传递实质是值传递
Java中进行赋值操作或函数调用中传递参数时,遵循值传递的原则:
基本类型数据传递的是该数据的值本身 
引用类型数据传递的是对对象的引用(句柄),而非对象本身 
方法只能改变引用类型的值,而不能改变引用类型的地址和基本类型的值

public class  HelloWorldApp 
{
public static void main(String[] args) 
{
int x=10;
int y=20;
System.out.println(“main:x=”+x+"\ty="+y);
swith(x,y);
System.out.println(“main:x=”+x+"\ty="+y);

Person p=new Person();
System.out.println(“main:Person name:”+p.getName());
change§;
System.out.println(“main:Person name:”+p.getName());
}

public static void swith(int x,int y){
x=x+y;
y=x-y;
x=x-y;
System.out.println(“swith:x=”+x+"\ty="+y);
}

public static void change(Person p){
p.setName(“李四”);
System.out.println(“change:Person name:”+p.getName());
}
}
class Person
{
private String name;

public void setName(String n){
name=n
}

public String getName(){
return name;
}
}

可变参数 

从JDK1.5以后,Java允许定义方法的参数长度可变的参数,从而允许为方法指定数量不确定的形参,如果在定义方法时,在最后一个参数的类型后增加三点(…),则表明该形参可以接受多个参数值,多个参数值被当成数组传入。
public class  Test
{
public static void main(String[] args) 
{
int[] arr={1,34,4,3,6,36};

add1(1,1,34,4,3,6,36);
add2(1,arr);
}

public static void add1(int x,int… y){
int temp=x;
for(int i:y){
temp+=i;
}
System.out.println(“total:”=temp);

}

public static void add2(int x,int[] y){
int temp=x;
for(int i:y){
temp+=i;
}
System.out.println(“total:”=temp);
}
}

===================== 这里是重点,考试要考 =============================

面向对象三大特性:

    1.封装
    2.继承
    3.多态

这里讲多态中的知识:

方法重载 

在java中,同一个类中的2个或2个以上的方法可以有同一个名字,只要它们的参数声明不同即可。在这种情况下,该方法就被称为重载(overloaded) 
a. 参数类型不同
b. 参数个数不同
c. 是否和作用域? ×
d. 是否和返回类型? ×

思考:为什么要用方法重载?
我们知道,在现实中,往往一个类会实现复杂的功能,其中定义的多种方法可能实现的功能意义都是一样,比如我们已经熟悉的System类中的静态方法println(),在该类中println()被定义了多个,每一个方法都有不同的参数,现在我们已知道每一个println()都具有相同的功能:在控制台上输出内容!
我们来假想一下,如果按照每个方法定义一个不同名称,那么我们将在System类中定义十多种不同名称的打印方法,虽然功能实现了,首先,我们是否需要编写代码前给这十几种方法取不同名称,并且还得保证名称唯一,这就会增加我们的工作量;其次我们还得记住每一个方法名对应的功能,如果稍有记错,那就会得到错误的结果!因此,我们有更好的解决办法,通过重载,可以在一个类中定义相同名称、不同参数的实现相同功能的多个方法,这样就避免了给每个方法取不同名称、熟记每个不同名的方法对应的功能的额外工作量,提高了我们的开发效率。

构造方法

对象的创建:
类名称 对象名称=new 类名称();如:Person p=new Person();
因为有”()”,所有表示的是一个方法,这实际上就是一个构造方法。
构造方法声明语法:
<修饰符> <类的名称>  ( <参数> ) {
<代码块>
}

一个对象的创建,必需要执行构造函数。
构造函数和类名同名,不能有返回类型,不能再构造方法中使用 return 返回一个值,满足其封装性。
Java为每一个类自动提供缺省构造函数。但是,一旦显式声明了自已的构造函数,缺省的构造函数将不复存在。
可以重载构造函数,构造方法的主要目的是为类中的属性初始化。既然是方法,则方法中肯定可以传递参数,传递不同类型或者个数参数实现重载。

this 关键字

在Java 中this关键字是最难理解的。因其语法较为灵活。
this 关键字有以下作用:
表示类中的属性; 
可以使用this 调用本类的构造方法; 
this 表示当前对象 

注意:在使用 this 关键字调用其他构造方法的时候,有以下几点限制; 
this()调用构造方法的语句只能放在构造方法的首行(非构造方法不可以使用)。 
至少有一个构造方法是不用 this调用的。 

变量

程序执行当中数值可变的数据称为变量,变量包括变量名称和变量值。在同一作用域里变量不能重复声明。变量名称区分大小写,变量必须先定义后使用。
只有定义过才能为其分配相应数量的存储单元。
一旦定义,变量类型才能确定,才能检查其运算的合法性
声明变量和赋值
结果:
int age = 20;
float fVar = 5.89f;
double dVar = 2.78d;
char cVar = ‘A’;

变量分为

成员变量,类中定义的变量被自动初始化
局部变量(方法或语句块内) ,局部变量必须要手工初始化
图2-9

Java成员变量初始化的三种方式

默认初始化
定义时初始化
构造函数初始化

每个变量都有一个生命周期,也就是说这个变量在哪个程序段中起作用。
变量的生命周期从它被声明时开始直到遇到声明变量的代码段的结束符(})为止。
只能在变量的生命周期内访问它。如果在生命周期之外访问变量,编译器将产生一个错误。

类的继承 

继承是面向对象的三大特征之一。
继承含义:
java中一个类只能继承于另一个类。我们将被继承的类称之为父类(基类),继承类称之为子类(派生类)。在java中用关键字extends来实现单继承,
语法如下:
class subclass extends superclass{…}
实现继承关系的类之间有着必然的联系,不能将不相关的类实现继承.
   比如:人类不能继承自鸟类 

下面,假设我们开发某公司的员工管理系统,已知类Manager和类Employee,代码如下:
class Employee {
public String f_name;
public String l_name;
public float salary = 0.0f;
public String getEmpDetails()
{…}
}

class Manager {
public String f_name;
public String l_name;
public float salary;
public String dept;
public String getEmpDetails()
{…}
}

通过分析得知,在类Employee和类Manager中存在许多共同的属性和行为,在现实生活中,Manager是公司Employee之一,因此,我们可以将Manager类定义成Employee类的子类,修改类Manager如下:
class Manager extends Employee {
public String dept;
public String getEmpDetails() {
return “This is Manager!”;
}
}

继承带来的一些好处

减少代码冗余
从上面的例子就可以看出,类Manager通过继承而无需再定义属性(f_name,l_name,salary),从而减少了代码量. 
维护变得简单
假设公司要求给所有员工添加生日这一属性,那么,在
没有继承时,我们的维护将变得困难(需修改每一个级别
的员工类)。 
扩展变得容易
当一个新的级别员工类需创建时,我们只需将该类继承所有员工父类Employee,接着再定义属于该员工的特有属性即可。 

判断下列代码判断是否正确:
片断一:
class B{…}
class C{…}
class A extends B,C
{…}
片断二:
class A{…}
class B extends A{…}
class C extends A{…}

提醒:构造方法不能被继承!一个类得到构造构造方法只有两种途径:自定义构造方法;使用JVM分配的缺省构造方法。但是,可以在子类中访问父类的构造方法,后面我们会深入。 

继承访问限制

访问限制 
在使用继承的时候也应注意:子类是不能直接访问父类中的私有成员的,也不能直接调用父类中的私有成员,但可以调用父类中的非私有方法。 

继承总结

作用:
通过继承可以简化类的定义
扩展类的功能
实现:
Class 子类 extends 父类
规则:
Java只支持单继承,不允许多重继承
Java允许多层继承
子类只能继承父类所有的公有成员。隐含的继承了父类中的私有属性

访问修饰符

在java中是通过各种访问区分符来实现数据封装的,共分为四种访问级别(访问控制级别从小到大):private(私有)、friendly(缺省)、protected(受保护)、public(公共)。
注意:以上四种访问修饰符可以作用于任何变量、属性和方法,类只可以定义为缺省或公共级别(嵌套类除外)。
图2-10

访问控制符的使用原则

类里的绝大部分属性都应该使用private修饰,除了一些static修饰的、类似全局变量的属性,才可以考虑使用public修饰。此外有些方法只是用来辅助实现该类的其他方法,这些方法被称为工具方法,工具方法也应该使用private修饰
如果某个类主要用做其他类的父类,该类里包含的大部分方法可能仅希望被其子类重写,而不想被外界直接调用,则应该使用protected修饰这些方法、
希望暴露出来给其他类自由调用的方法应该使用public修饰。类的构造器通过使用public修饰,暴露给其他类中创建该类的对象,因为顶级类通常都希望被其他类自由使用,所以大部分顶级类都使用public修饰。

覆盖是基于继承的,没有继承就没有覆盖。

高能预警!!!

下面是面向对象多态特性其中的另一个,重写。

在java中,覆盖的实现是在子类中对从父类中继承过来的非私有方法的内容进行修改的一个动作(注意:不能违反访问级别的限制)。 
方法的重写要遵循”两同两小一大”规则:
a. 重写(覆盖):子类和父类方法名相同,参数列表相同,这是”两同“。
b. 作用域:指的子类方法的访问权限应该比父类方法更大或者相等,这是”一大“。
c. 返回类型和抛出异常: ”两小“指的是子类方法返回值类型应该比父类方法返回值类型更小或者相等,子类方法声明抛出的异常应比父类方法声明抛出异常类更小或者相等。

注意:
覆盖方法和被覆盖方法要么都是类方法,要么都是对象方法,不能一个是类方法,一个是对象方法。

对于一个private方法,因为它仅在当前类中可见,其他子类无法访问该方法,所以子类无法重写该方法,如果在子类中定义一个与父类private方法有相同方法名、相同形参列表、相同返回值类型的方法,也不是方法重写,只是重新定义了一个新方法。

  **重载** **覆盖** **(** **重写** **)**
**单词** Overloading Override
**概念** 方法相同,

参数的类 型或个数不同

方法名称相同,参数类型及 个数相同,方法的访问 权限不能更加严格
**范围** 发生在一个类中,

也 可以在继承类里。

发生在继承关系中,是由子 类进行
### super 关键字

super是直接父类对象的默认引用。
在继承关系中,子类覆盖了父类方法产生了多态,但执行期间,确实要调用父类的方法。
在构造函数中,可以显式的调用父类构造函数。
图2-11

高能预警!!!

面向对象三大特性之——封装

封装性

封装是面向对象的三大特征之一。
为什么要封装?
封装是面向对象语言对客观世界的模拟,客观世界里的属性都是被隐藏在对象内部,外界无法直接操作和修改。实现良好的封装,需要从两个方面考虑:
1将对象的属性和实现细节隐藏起来,不允许外部直接访问,
2 把方法暴露出来,让方法来操作或访问这些属性。
封装实际有两个方面含义:该隐藏的隐藏起来,该暴露的暴露出来,

在继承中,为了保证父类良好的封装性,不会被子类随意改变,设计父类通常应该遵循如下规则:
尽量隐藏父类的内部数据,把父类的所有属性设置为private访问类型,不用让子类直接访问父类属性。
不要子类可以随意访问、修改父类的方法。父类中的那些辅助其他的工具方法,应该使用private访问控制阀修饰符,让子类无法访问该方法。如果父类的方法需要外部类调用,单不希望子类覆盖,可以使用 public final联合修饰,如果希望父类某个方法被子类重写,但不希望其他类自由访问,可以使用protected修饰。

实现封装
属性封装:
private 属性类型 属性名称=初始值;
方法封装:
private 方法返回值 方法名称(参数列表){//方法体}
访问
属性被封装一直如何访问:编写setter或者getter方法完成。
public属性类型  get首字母大写的属性名称(){ return 属性值;}
Public 属性类型 set首字母大写的属性名称(属性类型 参数){ 属性=参数值}
setter方法体里可以加上数据校验。

收藏

暂无评论

登录后可以进行评论。没有账号?马上注册