1. 类和对象的概念
计算机语言一般分为 面向对象 和 面向过程,Java 是一门面向对象的语言
面向过程编程
,追求算法优先,相信算法能解决一切问题,先确定如果操作数据,然后再指定操作数据的步骤
面向对象编程
,习惯于将问题分解,每个类解决几个问题,使用类创建对象,通过对象之间的交互来解决问题,且在几十个对象中找问题总比在几百个过程里找问题要方便
面向对象三大特征:封装、继承、多态
-
封装:将对象属性隐藏起来,不能直接访问对象属性,只能通过类方法来访问对象属性。这样可以阻止其它代码直接更改对象属性
-
继承:可以通过扩展其它类来构建新类,所有的类都始于一个超类 ”Object“
-
多态:多态是同一个行为具有多个不同表现形式或形态的能力,多态就是同一个接口,使用不同的实例而执行不同操作,具体的一个表现为 父类引用指向子类对象
类之间的关系:依赖(uses - a)、聚合(has - a)、继承( is - a)、关联、实现
预定义类:预定义类没有表现出明显的面向对象的特征,调用时只需要知道方法名和参数就可以通过类名来使用。如 Math 类的 random 方法,Math .random()
2. 类的编写常识
-
建议每个 java 文件只存放一个类
-
但如果一个 java 文件中存在多个类的时候,声明为 public 的类是主类,其他类是内部类,如果一个类想实现除它的外部类,不会被其他类使用时应该选择使用成员内部类
-
使用 var 来声明变量听说可以省略一些书写时间,但当用 eclipse 编写代码并开启自动补全之后,这个 var 用和不用没什么区别
-
每个类可以有一个 main 方法作为程序开始执行的地方,通常作为单元测试的一个技巧。如果要单独测试一个类可以在控制台执行
java className
,如果要测试一个复杂的程序,可执行java ApplicationName
,此时其他类的 main 方法永远也不会被执行 -
声明一个对象但没有对其进行初始化的时候,如果这是让这个对象调用某个方法,会放回 NullPointerException 异常
-
拒绝 null 参数
Objects.requireNonNull(n,"The name can not be null ");
-
不要返回可变对象的引用,会破坏类的封装性
公有继承 继承自父类的成员保持不变。
私有继承 继承自父类的成员全部变为私有成员。
保护继承 继承自父类的公有成员变为保护成员,其余不变。
- 对象变量并没有实际包含一个对象,它只是引用一个对象
3. 类基础
package chapter04;
/*
* NO:02
* name:Java 类基础
* describe:
* 类基本框架
*/
/*
* 权限::
* public:public表明该数据成员、成员函数是对所有用户开放的,
* 所有用户都可以直接进行调用
* private:private表示私有,私有的意思就是除了 class 自己之外,
* 任何人都不可以直接使用。
* protected:protected 对于子女、朋友来说,就是public的,
* 可以自由使用,没有任何限制,而对于其他的外部class,
* protected 就变成 private
* 缺省:
*/
public class ClassFramework {
// 类属性,一般将类属性设置为 private,禁止其它代码直接操作对象属性
// 类属性可以直接在这里赋初始值,但一般都在构造方法里初始化对象
private String name;
private int id;
// 构造方法,权限必须为 public,方法名必须与类名相同
// 在 Java 中需要注意的是 Java 中的对象都是在堆中构造的,
// 且构造器的总是结合 new 操作符一起使用
public ClassFramework(String name, int id)
{
// this 用在构造器或者方法中,用于获得调用当前的构造器方法的对象引用。
// 可以和任何的对象引用一样来处理这个this对象
this.name = name;
this.id = id;
}
// 类方法,每一个类的实例对象都能调用
// 调用方式 对象名.方法名()
public String getName()
{
return name;
}
// 一般每个属性都有相应的 get 和 set 方法
public void setName(String naem)
{
this.name = name;
}
// 也可以编写其它方法来对属性进行设置,只要注意别返回对象的引用即可
}
package chapter04;
import java.time.LocalDate;
/*
* NO:02
* name:Java 类基础
* describe:
* 公共类
* 内部类
* 参数(隐式与显式)
* 静态字段、静态方法、静态常量
*/
// 一个类文件有且只有一个 public 类,同一文件内其它类不可以声明为 public
public class EmployeeTest {
// 一般的类是没有 main 方法的,只有程序的入口才有 main 方法
// 在启动程序的时候还没有对象,静态的 main 方法将执行并构造程序所需要的对象
// 但如果有需要也可以在一般的类上使用 main 来对类本身进行测试
public static void main(String[] args) {
// 声明一个对象数组,这里创建来三个对象的引用,还没有开始创建对象
Employee[] staff = new Employee[3];
// 创建对象
staff[0] = new Employee("Zhang San",75000, 1999,12,15);
staff[1] = new Employee("Li Si", 50000, 2000,10,1);
staff[2] = new Employee("Wang Wu", 40000, 2004, 3, 13);
// 打印多有员工的信息
for(Employee e : staff)
System.out.println("name="+e.getName() + " ,salary="+e.getSalary() +
" ,hireDay="+e.getHireDay());
System.out.println("-------------------------------------");// 分隔
// for each 循环提高数组内所有对象的薪资 salary 5%
for(Employee e : staff)
e.raiseSalary(5);
// 打印多有员工的信息
for(Employee e : staff)
System.out.println("name="+e.getName() + " ,salary="+e.getSalary() +
" ,hireDay="+e.getHireDay());
}
}
// 内部类,有,但是很少使用,程序员更习惯于将不同的类存放在不同的文件中
class Employee
{
private String name; // 类属性,可以赋初始值,实例化的时候自动初始化
private double salary; // 但一般在构造函数那里赋初始值
private LocalDate hireDay;
private static int Eno = 1;// 在这里赋初始值的一般是静态变量,比如这个
// 静态变量只会初始化一次,该类的所有对象共用这个静态变量
// 构造方法。每个对象初始化时自动调用的方法
public Employee(String name, double salary, int year, int month, int day)
{
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year,month,day);
}
// 类方法,提b百分比高员工薪资
public void raiseSalary(double byPrecent)
{
salary += salary * byPrecent/100;
}
// 一般 IDE 选定属性,然后右键打开选项列表,找到 Generate
// 用 Generate 来自动创建 getter 和 setter 方法(如果想手动当我没说)
// 以后的文档中的部分代码不会展示 get 和 set 方法,但是要有
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
// 注意到这个 set 方法了吗,对象中的属性可以是对象,
// 初始化的时候需要使用更多的数据来初始化属性
// 可套娃
public void setHireDay(int year, int month, int day) {
hireDay = LocalDate.of(year,month,day);
}
public void setName(String name) {
this.name = name;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
4. 参数
参数可以分为 隐式参数 和 显式参数
staff[0].raiseSalary(5);
// 上面这条语句使用上一个类 EmployeeTest,其中内部类方法为 raiseSalary()
// 隐式参数 就是上面的 staff[0],它表示的是调用方法的发起者
// 隐式参数必须是一个对象,可以是 类名(如Math类) 或 对象名
// 显式参数 是上面 raiseSalary(5) 中括号里的 ‘5’,它被传入方法内部,等待处理
// 显式参数可以是 数据(数值),也可以是数据的引用(对象)
// Java 中的 对象引用 是 按值传递 的
-
call by value (按值调用):方法无法改变传递过去的值
-
call by reference (按引用调用):方法可以改变传过去的引用所指向的值
5. final
class Employee
{
Private final String name;
....
}
// 使用 final 声明类属性时,这个类属性必须在构造对象时初始化,且以后不能再修改这个值
6. 工厂方法
工厂方法有很多,大致分为三类:简单工厂模式(Simple Factory)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)。这三种工厂方法抽象层级依次提高
-
简单工厂模式(Simple Factory):简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例,由于创建实例的方法通常为静态(static)方法,因此简单工厂模式又被成为静态工厂方法模式(Static Factory Method)
-
工厂方法模式(Factory Method):进一步解耦合,把工厂类抽象,不再负责所有实例的创建,而是把具体的创建工作交给了子类去完成,实例化延迟到子类加载,由子类来决定要实例化的类
-
抽象工厂模式(Abstract Factory):抽象工厂将工厂方法进一步抽象。定义了一个接口用于创建相关或有依赖关系的对象簇,而无需指明具体的类。可以根据创建对象类型使用对应的工厂子类。将单个的简单工厂类变成了工厂簇,利于代码的维护和扩展
静态工厂方法的优点:
有名字,构造函数的名字都是一样的,但是如果使用静态工厂,可以自定义构造方法的名称而不混乱
基于上面的优点,甚至可以创建一些参数相同而名字不同的构造函数
易于实现单例控制模式
可以返回原返回类型的子类
7. 重载
如果有多个方法具有相同的名字,但参数不同,便出现了重载。编译器会根据传入的参数自动选择使用哪一个方法
重载可以在 构造方法、类方法 中使用
// 重载示例,indexOf()
// 需要注意的不是参数名,是参数类型,
// 当参数类型的排列顺序相同,编辑器无法判断需要具体执行哪个方法
indexOf(int)
indexOf(int, int)
indexOf(String)
indexOf(String, int)
// 重载 构造器
// 一般这叫子类构造器,但我觉得和重载没什么区别,起码形式上是一致的
public Manger(String name, double salary, int year, int month, int day)
{
// super 调用超类的构造方法
super(name,salary,year,month,day);
// 子类构造方法开始加料
bonus = 0;
}
// 可以套娃
8. 项目目录结构
项目中一般使用 ‘包’ 来组织项目目录结构,包 可以将类组织在一个集合中。
包可以用于确保类名的唯一性,如两个同名的类如果放在不同的包中将不会发生冲突
一般包名的定义为域名的逆序,如我的个人网站为 guyan.wang,na可以得到包名 wang.guyan
,然后还可以在其后追加一个工程名,如wang.guyan.personnel
,然后再把类 Employee 放到这个包中,这个类就被 完全限定
了,在整个项目中是独一无二的 别说在一个包下建同名类,在集成开发平台这个操作不可能成功
// 导入 Java 库中的包
// 如常使用 Scanner 对象,需要使用 import 关键字导入 java.util.Scanner 这个类
import java.util.Scanner;
// 如果使用多个 util 中的类,可以使用 ‘*’ 替代 Scanner,从而导入整个 util 包
import java.util.*;
// 注意,上面的 Scanner 表示的是导入这个类,util.* 表示的是导入 util 这个包
package wang.guyan.personel;
// 注意,如果规划了包结构,在类文件首部添加这一句是必要
// 当然,一般 IDE 会自动添加,不需要我们关注
9. 打包
不会有人将代码放到客户面前运行,展示运行效果,更多的是将这些众多的文件打包为一个归档文件,这个文件里可以包含 图像、声音和其它类型的文件
jar包:通常是开发时要引用通用类,打成包便于存放管理。
war包:是做好一个web应用后,通常是网站,打成包部署到容器中。
ear包:企业级应用,通常是EJB打成ear包。
注:所有的包都是用 jar 打的,只不过目标文件的扩展名不一样
打包过程
右键项目,打开Open Module Setting
;或点击上方菜单中的File
,打开Project Structure
,这两个看到的界面是一样的。依次选择Artifacts -> JAR -> From modules with ...
在Main Class
中选择程序的主类,一个项目中只有这个主类会被执行,也就是说这个主类的 main 方法会被当作程序的入口,其它类的 main 方法则会被忽视。
JAR files from libraries
是选择打包时是否带依赖库一起打包,extract to the target JAR
是将依赖库也提取出来一起打包,第二个则是两个分开打包。
Directory for META-INF...
可以选择打包后 JAR 包的存放位置,一般是直接放在项目下,后续会生成一个 out 目录。
选完这些之后,点击 OK,然后再次点击 OK 或 Apply 保存这个配置。
关闭上面的配置页面,点击菜单栏的Build
,选择Build Artifacts
继续选择Build
等待构建完成之后,就可以在项目目录之下看到生成了对应项目的 jar 包。
想要执行这个包,可以在命令行中执行java -jar 项目名.jar
,注意,使用时先切换到对应的工作目录或使用绝对路径。
10. 类设计提示
-
确保数据私有
-
显示地初始化数据,而不是依靠系统的默认值
-
不要在类中使用过多的基本类型
-
不是所有的字段都需要单独的字段访问器和字段更改器
-
分解有过多职责的类
-
类名和方法名要能体现其职责,起码不要取无意义的名字
-
有限使用不可变的类
参考:
- Java 核心技术 11版 Cay S.Horstmann 著 林琪 等译
- idea如何打包项目(java) - 三只坚果 - 博客园