标签:led 局部参数 pow 静态初始化 public 匹配 second @param 全面
面向对象程序设计(简称 OOP )是主流的程序设计范型,取代了早年的结构化过程化程序设计开发技术。Java 是完全面向对象的,必须熟悉 OOP 才能够编写 Java 程序。
面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。程序中的很多对象来自标准库,还有一些自定义的。在 OOP 中,不必关心对象的具体实现,只要能够满足用户的需求即可。
传统的结构化程序设计首先要确定如何操作数据,然后在决定如何组织数据,以便于数据操作。而 OOP 将数据放在第一位,然后在考虑操作数据的算法。
面向对象更加适用于解决规模较大的问题。比如,如果实现一个简单的 web 游览器可能需要大约 2000 个过程,这些过程需要对一组全局数据进行操作。采用面向对象的设计风格,可能只需要大约100个类、每个类平均包含20个方法,这更容易程序员掌握,也容易掌握 bug。对象的数据出错了,只需要在数据项的20个方法中查找错误,这比在2000个过程中查找容易的多。
类(class)是构造对象的模版或蓝图。由类构造(construct)对象的过程称为创建类的实例(instance)。标准的 java 库提供了几千个类。
封装(encapsulation)是与对象有关的一个重要概念。形式上,封装将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式。对象中的数据称为实例域(instance field),操纵数据的过程称为方法(method)。对于每个特定的类实例(对象)都有一组特定的实例域值。这些值的集合就是对象的当前状态(state)。无论何时,只要向对象发送一个信息,他的状态就有可能发生改变。
实现封装的关键在于绝对不能让类中的方法直接地访问其他类的实例域。程序仅通过对象的方法与对象数据进行交互。封装给对象赋予了“黑盒”特征,这是提高重用性和可靠性的关键。
可以通过扩展一个类来建立另外一个新的类,这个过程称为继承,所有的类都源自于一个超类 Object。
对象的三个主要特性:
同一个类的所有实例对象,具有家族式的相似性。对象的行为是用可调用的方法定义的。
对象保存着描述当前特征的信息,这是对象的状态,对象状态的改变必须通过调用方法实现。
对象的状态并不能完全描述一个对象。每个对象都有一个唯一的身份。作为类的一个实例,每个对象的标识永远是不同的,状态也尝尝存在着差异。
对象的关键特性在彼此之间项目影响着。例如,对象的状态影响它的行为(行为肯定会根据状态做出不同的操作)
面向对象程序设计,没有所谓的从顶部的 main 函数开始编写。首先从设计类开始,然后再往每个类中添加方法。
比如在订单处理系统中,有这样一些名词:
还有一些动词:
对应的名词是类的一个参数,动词应该是类的一个方法。
类之间常见的关系:
uses-a
has-a
is-a
依赖(dependence):是一种最明显的、最常见的关系。如,A类使用B类因为A对象需要访问B对象查看状态。如果一个类的方法操纵另一个类的对象,就可以说一个类依赖于另一个类。尽可能将相互依赖的类减至最少,让类之间的耦合度最小。
聚合(aggregation):聚合关系意味着类A的对象包含类B的对象。整体与部分之间是可以分离的。
继承(inheritance):如果类A扩展类B,类A不但包含类B继承的方法,还会拥有 一个额外的功能。
UML符号:
继承 ---------|>
接口实现 - - - - - |>
依赖 - - - - - >
聚合 <>---------
关联 -----------
直接关联 ---------->
并不是所有类都有面向对象特征,比如 Math 类,只需要知道方法名和参数,不需要知道了解它的具体过程,这正是封装的关键所在。Math 类只封装了功能,它不需要也不必隐藏数据。由于没有数据,因此也不必担心生成对象以及初始化实例域。
使用对象,首先构造对象,使用构造器构造新实例,然后可以对对象应用方法。
new Date();
new Date().toString();
通常,希望构造的对象可以多次使用,因此,需要将对象存放在一个变量中。此时,birthday 就是对象变量,对象变量初始化之后的值是对存储在另外一个地方的一个对象的引用。
Date birthday = new Date();
birthday.toString();
未初始化的对象变量不能调用对象的方法,否则会编译错误;对象变量如果赋值为null
则表明对象变量目前没有引用任何对象,此时调用对象的方法会产生运行错误。
Date birthday;
birthday.toString();//编译错误
birthday = null;
birthday.toString();//运行错误
Date类的实例有一个状态,即特定的时间点。
时间是用距离一个固定时间点的毫秒数(可正可负),这个点成为纪元,UTC 的时间为1970年1月1日 00:00:00
。
有个日历表示法类LocalDate
类,不要使用构造器来构造LocalDate
类的对象。
LocalDate.now();//静态工厂方法,表示构造这个对象时的日期。
LocalDate.of(1999, 12, 31);//使用年月日来构造一个日期。
LocalDate newYearsEve = LocalDate.of(1999, 12, 31);//保存在一个对象变量中。
int year = newYearsEve.getYear();//获取年
int month = newYearsEve.getMonthValue();//获取月
int day = newYearsEve.getDayOfMonth();//获取日
LocalDate aThousandDaysLater = new newYearsEve.plusDays(1000);//距离当前日期1000天的日期。
使用类的 get 方法可以返回对象的状态,使用类的 set 方法可以改变对象的状态。通常在更改器名前面加上前缀 set ,在访问器名前面加上前缀 get 。
想创建一个完整的程序,因该将若干类组合在一起,其中只有一个类有 main 方法。
简单的 Employee 类:
class Employee{
// instance fields
private String name;
private double salary;
private Date hireDay;
// constructor
public Employee(String n, double s, int year, int month, int day){
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
// method
public String getName(){
return name;
}
}
实际用处,构造了一个类数组,并填入了三个雇员信息。
Employee[] staff = new Employee[3];
staff[0] = new Employee("Carl Cracker",...);
staff[1] = new Employee("Harry Hacker",...);
staff[2] = new Employee("Tony Tester",...);
利用 raiseSalary 方法将每个雇员的薪水提高 5%
for (Employee e : staff){
e.raiseSalary(5);
}
最后调用,get 方法,或者,toString 方法将每个雇员的信息打印出来。
for (Employee e : staff){
System.out.println("name=" + e.getName()
+ ",salary=" + e.getSalary
+ ",hireDay=" + e.getHireDay());
// e.toString();
}
在一个源文件中,只能有一个公有类,但可以有任意数目的非公有类。
程序员习惯于将每一个类存在一个单独的文件中。
此时,就会有多个文件,当我们执行一个程序时,在命令行只需要编译程序的入口。当程序的入口使用到其他类时首先会查找.class
的文件。如果没有找到这个文件,就会自动搜索.java
文件,对它进行编译。如果.java
比.class
版本新,java 编译器会自动地重新编译这个文件。
我们可以在构造的同事给实例域初始化为所希望的状态。
构造器的注意点:
方法可以操作实例域,对象变量调用类的方法存取它的实例域。
类的方法有两种参数:
public void raiseSalary(boucle byPercent){
double raise = salary * byPercent / 100;
salary += raise;
}
每一个方法中,关键字 this 表示隐式参数,带上 this 可以将实例域与局部变量明显的区分开来。
如,在构造函数中,利用 this 将局部参数赋值给同名的隐式参数。
// instance fields
private String name;
private double salary;
private Date hireDay;
// constructor
public Employee(String name, double salary, int year, int month, int day){
this.name = name;
this.salary = salary;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
this.hireDay = calendar.getTime();
}
get 方法只返回实例域的值,又称为域访问器。
为了防止破坏这个域值的捣乱者,因该提供下面三项内容:
封装的优点:
注意:不要编写返回引用可变对象的访问器方法,会破坏程序的封装性,如果需要返回一个可变对象的引用,因该首先对它进行克隆,对象 clone 是指存放在另一个位置上的对象的副本。
class Employee{
public Date getHireDay(){
return hireDay.clone();
}
}
一个方法可以访问所属类的所有对象的私有数据。A类的方法可以访问A类的任何一个对象的私有域。
方法可以调用对象的私有数据。一个方法可以访问所属类的所有对象的私有数据。
在某些特殊情况下,可以将方法设计为私有。如一些辅助方法,它们只是用于自身的方法内使用,因该使用 private 的修饰符,这些辅助方法不应该成为公有接口的一部分。
如果未来这个类不需要这个私有方法,可以直接删去,如果是公有方法就不能随便删除,因为其他文件的代码有可能依赖它,如果是私有的我们完全不对单位外部对它的依赖。
实例域定义为 final ,每一个构造器执行之后,实例域的值被设置,并且后面的操作不能对它进行更改。
final 修饰符大都应用于基本类型域或不可变类的域(如 String)。
main 方法都被标记为 static 修饰符。
如果一个类中定义了一个静态域和一个实例域,并且生成了1000个类的对象,则此时有1000个实例域。但是只有一个静态域,即使没有一个类对象生成,静态域也存在。
class Employee{
private static int nextId = 1;
private int id;
}
静态变量使用的比较少,但静态常量却使用的比较多,例如,Math 类中的 PI 。
好处:
public class Math{
public static final double PI = 3.14.....;
}
静态方法是一种不能向对象实施操作的方法,也就是没有隐式的参数,可以认为静态方法是没有 this 参数的方法,但是静态方法可以访问静态域。
public static int getNextId(){
return nextId;
}
以下两种情况使用静态方法:
一个类可以使用静态工厂方法来构造对象。
public final class LocalDate
implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {
...
public static LocalDate now(Clock clock) {
Objects.requireNonNull(clock, "clock");
// inline to avoid creating object and Instant checks
final Instant now = clock.instant(); // called once
ZoneOffset offset = clock.getZone().getRules().getOffset(now);
long epochSec = now.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later
long epochDay = Math.floorDiv(epochSec, SECONDS_PER_DAY);
return LocalDate.ofEpochDay(epochDay);
}
public static LocalDate of(int year, Month month, int dayOfMonth) {
YEAR.checkValidValue(year);
Objects.requireNonNull(month, "month");
DAY_OF_MONTH.checkValidValue(dayOfMonth);
return create(year, month.getValue(), dayOfMonth);
}
...
}
这样构造日期对象:
LocalDate.now();//静态工厂方法,表示构造这个对象时的日期。
LocalDate.of(1999, 12, 31);//使用年月日来构造一个日期。
按值调用:方法接收的是调用者提供的值。
按引用调用:方法接收的是调用者提供的变量地址。
方法参数共有两个类型:基本数据类型、对象引用。
java 语言总是采用按值调用的程序设计语言。
方法参数分为两种类型:
假设有一个方法可以将参数值增加3倍:
public static void tripleValue(double x){
x = 3 * x;
}
double percent = 10;
tripleVlaue(percent);
调用这个方法之后,percent
的值还是 10 。
执行过程:
总结:一个方法不肯能修改一个基本数据类型的参数。
以下代码可以将雇员的薪水提高两倍:
public static void tripleSalary(Employee x){
x.raiseSalary(200);
}
Employee harry = new Employee(...);
tripleSalary(harry);
执行过程:
总结:方法得到了对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。
引用类型获取的不是对象的地址,得到的是对象引用的拷贝,对象引用及其他的拷贝引用同一个对象。
下面一个例子来说明 java 不是按引用传递:
private static void swap(Employee x, Employee y) {
// 此时,x 指向 对象 a,y 指向对象 b
// 赋值,改变了 x 与 y 引用的对象,但是 a 与 b 引用的对象不变。
// 所以 java 都是按值传参的
Employee temp = x;
x = y;
y = temp;
// 此时,x 指向 对象 b,y 指向对象 a
// 如果 x 与 y 使用 set 改变了引用对象的值, a 与 b 引用的对象也改变,因为他们引用同一个对象。
x.setName("x"); // 改变了对象 b 的值
y.setName("y"); // 改变了对象 a 的值
}
public static void main(String[] args) {
Employee a = new Employee("a");
Employee b = new Employee("b");
//此时,a 指向对象a,b 指向对象 b
swap(a,b);
//此时,a 指向对象 a,b 指向对象 b
System.out.println(a.getName());// y
System.out.println(b.getName());// x
}
总结方法参数的使用:
如果多个方法有相同的名字、不同的参数(不同的参数类型或数量),便产生了重载。
编译器通过各个参数给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选相应的方法。
注意:实现重载返回值也可以不同。但只有返回值不同不是重载的要求,不能有两个名字相同、参数类型也相同却返回不同类型值的方法。
如果构造器没有显式地给域赋予初值,那么就会被自动地赋予默认值(0、false、null)。这是一个不好的习惯,如果一个对象默认值为 null ,调用 get 方法会得到一个 null 引用,这不是我们所希望的结果。
即默认的构造器,当没有其他构造器时,系统默认自动创建,这个构造器将所有的实例域设置为默认值。
确保不管怎么调用构造器,每个实例域都有自己的初始值,可以在类定义中将一个值赋给任何域。
class Employee{
private String name = "";
}
参数变量用同样的名字将实例域屏蔽起来,可以使用 this 指定隐式参数。
public Employee(String name, double salary){
this.name = name;
this.salary = salary;
}
关键字 this 可以引用方法的隐式参数,还有另一个含义,如果构造函数的第一句是this(...)
,这个构造器将调用同一个类的另一个构造器。
public Employee(double s){
this(name, s);
name++;
}
采用这种方法使用 this 关键字非常有用,这样对公共的构造器代码部分只编写一次即可。
三种初始化数据域的方法:
在一个类的声明中,可以包含多个代码块。只要构造类的对象,这些代码块就会被执行。
class Employee{
private static int nextId;
private int id;
private String name;
private double salary;
{
id = nextId;
nextId++;
}
public Employee(String n, double s){
name = n;
salary = s;
}
public Employee(){
name = "";
salary = 0;
}
}
这种机制不是必须的,也不常见,通常都是将初始化代码放到构造器中。
在Java中,有两种初始化块:静态初始化块和非静态初始化块
静态初始化块:使用static
定义,当类装载到系统时执行一次。若在静态初始化块中想初始化变量,那仅能初始化静态类变量,即static修饰的数据成员。
非静态初始化块:在每个对象生成时都会被执行一次,可以初始化类的实例变量。
生成对象时,执行的顺序:
存在继承关系时,父类与子类的执行顺序:
先初始化父类的静态代码 ---> 初始化子类的静态代码 --->
初始化父类的非静态代码 ---> 初始化父类构造函数 --->
初始化子类非静态代码 ---> 初始化子类构造函数
C++有显式的析构器方法,放置一些不再使用时需要执行的清理代码。
java有自动的垃圾回收器,不需要人工回收内存,所以 java 不支持析构器。
可以给java的类添加 finalize 方法,他会在垃圾回收器清除对象之前调用。
java 允许使用包将类组织起来,便于组织代码,分开管理,使用包的主要原因是确保类名的唯一性。
可以使用嵌套层次组织包,所有标准的 Java 包都处于 java 和 javax 包层次中。
常用的 java 类库的包:
java.lang -- 语言包:Java语言的基础类,包括Object类、Thread类、String、Math、System、Runtime、Class、Exception、Process等;
java.io -- 输入输出包:提供与流相关的各种包;
java.awt -- 抽象窗口工具包:Java的GUI类库,一般网络开发用不上
java.util -- 实用工具包:Scanner、Date、Calendar、LinkedList、Hashtable、Stack、TreeSet等;
java.net -- 网络功能包:URL、Socket、ServerSocket等;
java.sql -- 数据库连接包:实现JDBC的类库;
java.text -- 文本包:Format、DataFormat等。
一个类可以使用所属包中的所有类,以及其他包的公有类。
访问别的包公有类的方法:
java.util.Date today = new java.util.Date();
import java.util.Date;
Date today = new Date();
使用*
可以导入一个包中的所有类
import java.util.*
但是不能用*
导入以java为前缀的包,如:
import java.*;
import java.*.*;
如果两个包中有相同类名,会发生冲突,导致出错,如,util与sql都有Date类:
import java.util.*;
import java.sql.*;
Date today;// ERROR java.util.Date or java.sql.Date ?
//可以增加一个特性的类解决
import java.util.*;
import java.sql.*;
import java.util.Date;
Date today;
//如果都需要用到就要加上特定的类名了
java.util.Date deadline = new java.util.Date();
java.sql.Date today = new java.sql.Date(...);
import 还可以导入静态方法和静态域。
如在开通增加一条指令:
import static java.lang.Math.*;
就可以直接使用 Math 的静态域和静态方法,如
sqrt(pow(x, 2) + pow(y, 2))
直接看代码:
package com.xul.javaPrimary;
此时类文件放入了对应的包里,默认情况下,类放在没有名字的默认包里(defaulf package)。
标记为 public 的部分可以被任意的类使用;
标记为 private 的部分只能被定义他们的类使用;
没有定义修饰符的可以被同一个包中的所有方法访问。
Java 类路径告诉 java 解释器和 javac 编译器去哪里找它们要执行或导入的类。类(您可能注意到的那些 *.class 文件)可以存储在目录或 jar 文件中,或者存储在两者的组合中,但是只有在它们位于类路径中的某个地方时,Java 编译器或解释器才可以找到它们。
java -classpath /home/user/classdir:.:/home/user/archives/archive.jar MyProg
java -classpath c:\classdir;.;c:\archives\archive.jar MyProg
JDK 的工具 javadoc,可以由源文件生成一个 HTML 文档。
javadoc 实用程序从下面几个特性中抽取信息:
注释以/**
开始,并以*/
结束。
文档后面紧跟自由格式文本,标记由@
开始。
第一句通常是概要性的句子,可以通过 HTML 标签修饰
类注释必须要 import 语句之后,类定义之前。
/**
* adfasdfasdfasdfasdf
* adfsdfasfsdafsadfsdaf
*/
public class Card{
...
}
方法注释必须放在所描述的方法之前。除了通用标记外,还可以使用下面标记:
只需要对公有域(通常为静态常量)建立文档。
可以在类文档中的标记:
可以用于所有文档中的标记:
@since 文本 对引入特性的版本描述
@deprecated 文本 标记对类、方法或变量添加一个不再使用的注释,应给出取代建议。
@deprecated Use <code> setVisible(true) </code> instead
@see 和 @link 标记,增加超链接
可以直接将类、方法和变量的注释放到源文件中就可以了。想要产生包注释,就需要在每一个包目录中添加一个单独的文件。
<body></body>
之间所有文本都会被抽取出来。/** .. */
的 javadoc 注释,跟随在一个包语句之后。还可以为所有的源文件添加概述性的注释。放置在名为 overview.html 文件中。位于包含所有文件的父目录中,在标记<body></body>
之间所有文本都会被抽取出来。用户从导航栏中点击 Overview 时就会被显示。
假设抽取的文件放到 docDirectory 下:
切换到包含要生成文档的源文件目录。
如果是一个包
javadoc -d docDirectory nameOfPackage
如果文件在默认包中
javadoc -d docDirectory *.java
让类更有 OOP 专业水准:
标签:led 局部参数 pow 静态初始化 public 匹配 second @param 全面
原文地址:https://www.cnblogs.com/lovezyu/p/9127452.html