目录
四、面向对象(上)
1. 面向对象学习概述
面向过程 : 强调的是功能行为,以函数为最小单位,考虑怎么做。
面向对象 : 强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
举例 : "人把大象装进冰箱"
程序员从面向过程的执行者转换为了面向对象的指挥者
面向对象的学习主线 (第4-6章) :
-
java类及其类的成员 : 属性 方法 构造器 ; 代码块 内部类
-
面向对象的三大特征 : 封装 继承 多态 (抽象性)
-
其他关键字 : this、super、static、abstract、interface、package、import
面向对象的两个要素 :
-
类 对一类事物的描述 是抽象的、概念上的定义
-
对象 是实际存在的该类事物的每个个体, 因而也称为实例 (instance)
面向对象程序设计的重点是类的设计, 设计类, 就是设计类的成员。
理解"万事万物皆对象"
如何理解万事万物皆对象 ?
-
在java语言范畴中, 我们刻画功能结构都将其封装到一个类中, 通过类的实例化, 来调用具体的功能结构。
-
涉及到java语言与前端HTML、后端的数据库交互的时候, 前后端的结构在java层面交互时, 都体现为类和对象。例如与数据库中的Customer表交互时,java中Customer表对应一个Customer类,表中的一行数据对应java中的一个对象。
Scanner , String 等
文件 : File
网络资源 : URL
2. 类和对象
类中两个最主要的成员 : 属性(field)、方法(method)。
创建类的对象等同于类的实例化 = 实例化一个类
类和对象的使用 (面向对象思想落地的实现) :
-
创建类, 设计类的成员
-
创建类的对象
-
通过 "对象.属性" 或者 ”对象.方法“调用对象的结构
如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的) 意味着 :如果修改了一个对象的属性 a , 则不影响另一个对象的属性 a 的值
对象的内存解析 : 在栈中定义的指针指向堆中的对象
代码体现 :
public class PersonTest {
public static void main(String[] args) {
//创建Person 类的对象
Person p1 = new Person();
//调用属性和方法
p1.age = 17;
p1.name = "Tom";
p1.isMale = true;
System.out.println(p1.name);
//调用方法 对象.方法
p1.eat();
p1.sleep();
p1.talk("Chinese");
Person p2 = new Person();
System.out.println(p2.name);//null 默认初始化值
System.out.println(p2.isMale);//false
//System.out.println(p2);//com.atguigu.OOP1.Person@1b6d3586
//将p1变量保存的对象地址值赋值给p3, 导致p1和p3 指向了堆空间中同一个对象的实体
Person p3 = p1;
System.out.println(p3.name);
p3.age = 19;
System.out.println(p3.age);
}
}
class Person {
//属性
String name;
int age;
boolean isMale;
//方法
public void eat(){
System.out.println("can eat");
}
public void sleep(){
System.out.println("can sleep");
}
public void talk(String language){
System.out.println("can talk, use " + language);
}
}
JVM内存结构 :
对象的内存解析 :
编译完源程序以后,生成一个或者多个字节码文件。
使用JVM中的类加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析。
3. 属性和方法
3.1 类中属性的声明和使用
属性(成员变量) VS 局部变量
相同点
-
定义格式
-
先声明后定义
-
变量都有其对应的作用域
不同点
-
在类中声明的位置不同 属性直接定义在类的大括号内 局部变量定义在方法内、方法形参、代码块内、构造器内、构造器形参
-
关于权限修饰符的不同
属性可以声明的时候指明其权限, 使用权限修饰符 常用的权限修饰符 :private public 缺省 protected (没写就是缺省)
局部变量不能写权限修饰符
-
默认初始化值的情况
属性 : 类的属性根据其类型都有默认初始化值 与一维数组的一样
-
整型 : 0
-
浮点型 : 0.0
-
char : '\u0000' 或 ASCII码的0
-
bool : false
-
引用数据类型(类 数组 接口) : null
局部变量 : 没有初始化值, 在调用局部变量之前一定要显示赋值, 特别的, 形参在调用的时候赋值即可
-
-
二者在内存中加载的位置不一样
属性 : 加载到堆区
局部变量 : 加载到栈空间
以上要求都是非static的变量
3.2 类中方法的声明和使用
方法 :描述类具有的功能
方法的声明 :
权限修饰符 返回值类型 方法名(形参列表) {
方法体
}
return 关键字的使用
常用的权限修饰符 :private public 缺省 protected (没写就是缺省)
注意 : 关键词 static final abstract 来修饰的方法
在方法的使用中,可以调用当前类中的属性或方法。
例 : 利用面向对象的编程方法,设计类Circle,计算圆的面积。
public class Test {
public static void main(String[] args) {
//The test for circle
Circle c1 = new Circle();
c1.addR(5);
System.out.println("The radius of circle1 is " + c1.getRadius());
System.out.println("The area of circle1 is " + c1.getArea());
System.out.println("********************************");
Circle c2 = new Circle(10);
System.out.println("The radius of circle2 is " + c2.getRadius());
System.out.println("The area of circle2 is " + c2.getArea());
}
}
class Circle{
//field : the radius of circle
private double radius;
//method : calculate the area of circle.
// get the radius of circle.
// add the radius of circle.
public Circle() {}
public Circle(double r){
this.radius = r;
}
public void addR(double r){
this.radius = r;
}
public double getRadius(){
return this.radius;
}
public double getArea(){
return Math.PI * this.radius * this.radius;
}
}
4. 匿名对象的使用
-
理解 : 我们创建(new)的对象没有显示的赋值给一个一个变量名 就叫做匿名对象
-
特征 : 匿名对象只能调用一次
-
使用 : 直接用方便
代码体现 :
public class AnonymousObject {
public static void main(String[] args) {
Phone p = new Phone();//有名字的
p.sendEmail();
p.playGame();
new Phone().sendEmail();//匿名对象
new Phone().playGame();//匿名对象
//上面两个不是一个对象 是两个对象 之间没有关系
PhoneMall mall = new PhoneMall();
mall.show(new Phone());//匿名对象的使用
//在外面声明一个匿名对象 然后把地址传给形参 这样这个地址就有名字了 比较方便不用定义两个变量了
}
}
/**
* PhoneMall手机商场类
*/
class PhoneMall{
/**
* 展示手机的功能
* @param phone Phone 对象
*/
public void show(Phone phone){
phone.sendEmail();
phone.playGame();
}
}
class Phone{
//field
double price;//价格
//method
public void sendEmail(){
System.out.println("发送邮件");
}
public void playGame(){
System.out.println("玩游戏");
}
}
5. 详谈方法
5.1 方法的重载
-
定义 : 在同一个类中允许存在一个以上的同名方法, 只要他们参数个数或者参数类型不同即可
“两同一不同” : 同一个类相同的方法名 参数列表不同 :
参数列表中形参的 (数据类型、个数、顺序) 不同,就可认为重载
注意 : 只是返回值类型不同不算重载
-
举例 : Arrays类中重载的sort方法
-
判断是否是重载 跟权限修饰符 返回值类型 形参变量名 方法体都没有关系!
-
在通过对象调方法的时候 : 如何确定某一个指定的方法
-
看方法名
-
看参数列表
所以,方法的重载只能由参数列表的不同来决定,如果只是返回值不同的话,编译器不能辨认要使用哪个方法。
-
public class OverLoadTest {
public static void main(String[] args) {
OverLoad test = new OverLoad();
test.getSum(1, 2);//两个都是整型 输出 1 优先确定的来 如果没有 自动类型提升为剩下的double的
test.getSum(1.1, 1.2);//两个都是浮点型 输出 2
}
}
class OverLoad{
//如下方法构成了重载
public void getSum(int i, int j){
System.out.println("1");
}
public void getSum(double i, double j){
System.out.println("2");
}
public void getSum(int i, double j){
System.out.println("3");
}
}
public class OverloadExer {
public static void main(String[] args) {
OverloadExer test = new OverloadExer();
System.out.println(test.max(7, 6));
System.out.println(test.max(7.5, 7.3));
System.out.println(test.max(7, 6, 1.1));
}
public int max(int x, int y){
return (x > y) ? x : y;
}
public double max(double x, double y){
return (x > y) ? x : y;
}
public double max(double x, double y, double z){
return (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z);
}
}
5.2 可变形参的方法
JavaSE5.0中提供了Varargs机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
//JDK5.0以前 : 采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a, String[] books);
//JDK5.0 : 采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a, String ... books);
可变个数形参的方法 :
-
jdk 5.0 新增内容
-
具体使用 :
-
可变个数形参的格式 : 数据类型 ... 变量名
-
当调用可变个数形参的方法时,传入的参数个数可以是 : 0、1、2...多个
-
可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
-
可变个数形参的方法与本类中方法名相同,形参类型也相同的数组的方法之间不构成重载,二者不共存
-
在可变个数形参的方法内部使用形参,按照数组的使用方法来使用
-
可变个数形参在方法的形参中,必须声明在形参列表的末尾,且最多只能声明一个可变形参
-
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
test.show(12);
test.show("hello");
test.show();
test.show("hello", "world");
//可以调用形参为String数组的方法 也可以调用可变个数形参的方法
// test.show(new String[]{"AA", "BB"});
}
public void show(int x){
System.out.println("show(int x)");
}
public void show(String str){
System.out.println("show(String str)");
}
//可变个数的形参
// 可以匹配 0 个或多个 当输入的参数有具体对应重载的方法时,优先考虑具体的重载方法
public void show(String ... strs){
System.out.println("show(String ... strs)");
}
//以上方法不能与以下方法共存
// public void show(String[] strs){
// System.out.println("show(String[] strs)");
// }
public void show(int i, String ... strs){
System.out.println("show(int i, String ... strs)");
}
}
5.3 方法参数的值传递机制
关于变量的赋值 :
如果变量是基本数据类型, 此时赋值的是变量所保存的数据值。
如果变量是引用数据类型, 此时赋值的是变量所保存数据的地址值。
public class ValueTransferTest {
public static void main(String[] args) {
System.out.println("基本数据类型 : ");
int m = 10;
int n = m;
System.out.println("m = " + m + ", n = " + n);
n = 20;
System.out.println("m = " + m + ", n = " + n);// m 不受 n 的影响
System.out.println("引用数据类型 : ");
Order o1 = new Order();
o1.orderId = 1001;
Order o2 = o1;//赋值以后,o1 和 o2 的地址值相同,都指向了堆空间中的同一个对象实体
System.out.println("o1.orderId = " + o1.orderId + ", o2.orderId = " + o2.orderId);
o2.orderId = 1002;
System.out.println("o1.orderId = " + o1.orderId + ", o2.orderId = " + o2.orderId);
}
}
class Order{
int orderId;
}
方法形参的传递机制 : 值传递
-
形参 : 方法定义时, 声明的小括号内的参数
实参 : 方法调用时, 实际传递给形参的数据
-
值传递机制 :
如果参数是基本数据类型,此时实参给形参的是实参真实存储的数据值。
public class ValueTransferTest1 { public static void main(String[] args) { //交换两个变量的值 int m = 10; int n = 20; System.out.println("m = " + m + ",n = " + n); swap(m, n); System.out.println("m = " + m + ",n = " + n); } public static void swap(int m, int n){ int temp = m; m = n; n = temp; } } /* result : m = 10,n = 20 m = 10,n = 20 */
内存结构解析 :
在调用 swap 方法时,方法的形参会新生成两个 int 类型变量 m 、n。在方法内,形参的 m 、n 得到交换,与main方法中的 m 、n 无关,只是将其值传给了形参而已。
如果仍然想要使用方法来交换main方法中 m 、n的值,那么就将其封装在类中,通过对象 . 属性来交换两个变量的值 :
public class ValueTransferTest2 { public static void main(String[] args) { Data data = new Data(); data.m = 10; data.n = 20; System.out.println("m = " + data.m + ",n = " + data.n); //交换 m, n的值 swap(data); System.out.println("m = " + data.m + ",n = " + data.n); } public static void swap(Data data){ int temp = data.m; data.m = data.n; data.n = temp; } } class Data{ int m; int n; } /* result : m = 10,n = 20 m = 20,n = 10 */
如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。
public class ValueTransferTest3 { public static void main(String[] args) { int[] arr1 = new int[]{0, 0, 0, 0, 0}; int[] arr2 = new int[]{1, 1, 1, 1, 1}; System.out.println(Arrays.toString(arr1)); System.out.println(Arrays.toString(arr2)); swap(arr1, arr2); System.out.println(Arrays.toString(arr1)); System.out.println(Arrays.toString(arr2)); } public static void swap(int[] arr1, int[] arr2){ int[] temp = arr1; arr1 = arr2; arr2 = temp; } } /* result : [0, 0, 0, 0, 0] [1, 1, 1, 1, 1] [0, 0, 0, 0, 0] [1, 1, 1, 1, 1] */
内存结构解析 :
在调用 swap 方法时,方法的形参会新生成两个 int[] 类型变量 arr1 、arr2。在方法内,形参的 arr1 、arr2 的值交换,与main方法中的 arr1、arr2 无关,只是将地址值赋值给形参而已。
同样,如果仍然想要使用方法来交换main方法中 arr1 、arr2,那么就将其封装在类中,通过对象 . 属性来交换两个变量的值 :
public class ValueTransferTest4 {
public static void main(String[] args) {
int[] arr1 = new int[]{0, 0, 0, 0, 0};
int[] arr2 = new int[]{1, 1, 1, 1, 1};
Arr arr = new Arr();
arr.arr1 = arr1;
arr.arr2 = arr2;
System.out.println(Arrays.toString(arr.arr1));
System.out.println(Arrays.toString(arr.arr2));
swap(arr);
System.out.println(Arrays.toString(arr.arr1));
System.out.println(Arrays.toString(arr.arr2));
}
public static void swap(Arr arr){
int[] temp = arr.arr1;
arr.arr1 = arr.arr2;
arr.arr2 = temp;
}
}
class Arr{
int[] arr1;
int[] arr2;
}
/*
result :
[0, 0, 0, 0, 0]
[1, 1, 1, 1, 1]
[1, 1, 1, 1, 1]
[0, 0, 0, 0, 0]
*/
注 : String 是特殊的引用数据类型
String的值实际存放在方法区中的字符串常量池中,我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串(这点对理解上面至关重要)。
public class Test {
public static void main(String[] args) {
String str = "hello";
System.out.println(str);
test(str);
System.out.println(str);
}
public static void test(String s){
s = "hi";
}
}
/*
result :
hello
hello
*/
/*
表明main方法中str中的地址值对应的字符串没有被修改
而test方法中的s则是重新找了一块地址存放 "hi"
*/
String的底层是char[] 类型数组,由于数组不能动态扩充等操作,所以在对String数据类型做修改的时候,都是重新实例化的,不是修改地址对应的char[] 数组的值。
5.4 递归方法
递归方法的使用 :
-
递归方法 : 一个方法体内调用它自身。
-
方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环的控制。
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
<1> 计算 1 - 100 之间所有自然数的和
public class RecursionMethodTest {
public static void main(String[] args) {
int sum = sum(100);
System.out.println(sum);
}
public static int sum(int n){
if (n == 1) return 1;
else return n + sum(n-1);
}
}
<2> 已知一个数列 : f (0) = 1, f (1) = 4, f (n+2) = 2 * f (n+1) + f (n) . 其中n是大于0的整数,求f (10)的值
public class RecursionMethodTest {
public static void main(String[] args) {
int result = getResult(10);
System.out.println(result);
}
public static int getResult(int n){
if (n == 0) return 1;
else if (n == 1) return 4;
else return 2 * getResult(n-1) + getResult(n-2);
}
}
6. 封装和隐藏
6.1 封装性的引入
程序设计追求 : 高内聚、低耦合
-
高内聚 : 类的内部数据操作细节自己完成,不允许外部干涉
-
低耦合 : 仅对外暴露少量的方法用于使用
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
问题的引入 : 当我们创建一个类的对象以后,我们可以通过 “ 对象 . 属性 ” 的方式,对对象的属性进行赋值。这里,赋值操作仅受到属性的数据类型和存储范围的制约。除此之外,没有其他制约条件。但是在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。同时,我们需要避免用户再使用 " 对象 . 属性 " 的方法对属性进行赋值。则需要将属性声明为私有的。此时,针对属性就体现了封装性。
封装性的体现之一 : 我们将类的属性私有化 ( private ) ,同时提供公共的 ( public ) 方法来获取和设置属性的值。
拓展 : 封装性的其他体现
-
不对外暴露的私有方法。
-
单例模式,将构造器私有化。
-
等等......
代码体现 :
public class AnimalTest {
public static void main(String[] args) {
Animal a = new Animal();
a.setName("Tom");
a.setAge(-2);
a.setLegs(-9);
a.show();
// a.legs = 10; //报错
a.setAge(2);
a.setLegs(4);
a.show();
}
}
class Animal{
private String name;
private int age;
private int legs;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
if (age >= 0 && age <= 120) this.age = age;
else age = 0;//这里可以抛出一个异常,但是还未学到
}
public void setLegs(int legs) {
if (legs >= 0) this.legs = legs;
else legs = 0;//这里可以抛出一个异常,但是还未学到
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getLegs() {
return legs;
}
public void show(){
System.out.println("name : " + name + ", age : " + age + ", legs : " + legs);
}
}
/*
result :
name : Tom, age : 0, legs : 0
name : Tom, age : 2, legs : 4
*/
6.2 四种权限修饰符
封装性的体现,需要权限修饰符的配合。
Java规定四种权限 (从小到大排列) : private、缺省、protected、public
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
四种权限可以用来修饰类及类的内部结构 : 属性、方法、构造器、内部类。但修饰类的话,只能用 : 缺省、public
用public方式修饰类时,在包外加入import则可调用这个类。
用缺省方式修饰类时,在包外则不可调用这个类。
-
private : 类内部随便调
-
缺省 : 同一个包随便调
-
protected : 最大到包外子类随便调
-
public : 随便调
总结封装性 : Java提供了四种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。
7. 构造器或构造方法
constructor
7.1 构造器的作用
-
用来创建对象 : new + 构造器
-
初始化对象的属性
说明 :
-
如果没有显示的定义类的构造器的话,则系统默认提供一个空参的构造器,构造器的权限和类的权限相同
-
定义构造器的格式 : 权限修饰符 类名( 形参列表 ) { }
-
一个类中定义的多个构造器,彼此之间构成重载
-
一旦显示的定义了类的构造器以后,系统就不再提供默认的空参构造器
-
一个类中,至少会有一个构造器
public class TriAngleTest {
public static void main(String[] args) {
TriAngleTest test = new TriAngleTest();
double ret = test.getArea(new TriAngle(4, 5));
System.out.println("ret = " + ret);
}
public double getArea(TriAngle triAngle){
return (triAngle.getBase() * triAngle.getHeight()) / 2.0;
}
}
class TriAngle{
//Field
private int base;//底边长
private int height;//高
//Method
public void setBase(int base) {
this.base = base;
}
public void setHeight(int height) {
this.height = height;
}
public int getBase(){
return base;
}
public int getHeight() {
return height;
}
//Constructor
public TriAngle(){
this.base = 0;
this.height = 0;
}
public TriAngle(int base, int height){
this.base = base;
this.height = height;
}
}
总结 : 属性赋值的先后顺序
-
默认初始化
-
显示初始化
-
构造器中初始化
-
通过 "对象 . 属性" 赋值
以上操作的先后顺序 : ① --> ② --> ③ --> ④
7.2 拓展知识 : JavaBean
JavaBean是一种Java语言写成的可重用组件。
所谓JavaBean,是指符合如下标准的Java类 :
-
类是公共的
-
有一个无参的公共的构造器
-
有属性,且有对应的 get、set 方法
用户可以使用 JavaBean 将功能、处理、值、数据库访问和其他任何可以用 Java 代码创造的对象进行打包,并且其他的开发者可以通过内部的 JSP 页面、Servlet、其他 JavaBean、applet 程序或者应用来使用这些对象。用户可以认为 JavaBean 提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
例如和数据库交互的时候,一个表可以用 JavaBean 来造对象、获取数据等。
7.3 UML类图
7.4 this关键字的使用
-
它在方法内部使用,即这个方法所属对象的引用
-
它在构造器内部使用,表示该构造器正在初始化的对象
-
this表示当前对象,可以调用类的属性、方法和构造器
-
当在方法内需要用到调用该方法的对象时,就用this
this 理解为当前对象,在类的方法和构造器中,我们可以使用 ''this . 属性'' 或 ''this . 方法'' 的方式,调用当前对象的属性和方法。但是通常情况下,我们都选择省略 ''this . '',如果方法或构造器的形参和类的属性同名时,我们必须显示的使用''this . '',表示此变量是属性而非形参。
this 调用构造器,用来重载构造器时,减少代码的冗余 :
-
我们可以通过 "this ( 形参列表 ) "方式,调用本类中指定的其他构造器
-
构造器中不能通过 "this ( 形参列表 ) "方式,调用自己
-
如果一个类中有n个构造器,那么最多只能有 n - 1 个构造器使用了"this ( 形参列表 ) "
-
规定 : "this ( 形参列表 ) "必须声明在当前构造器的首行
-
构造器内部最多只能声明一个 "this ( 形参列表 ) " ,用来调用其他的构造器
this()和super()为构造方法,作用是在JVM堆中构建出一个对象。因为避免多次创建对象,所以一个方法只能调用一次this()或super()。this()和super()的调用只能写在第一行,避免操作对象时对象还未构建成功。而且this()和super()不能同时出现。
public class ThisTest {
public static void main(String[] args) {
This test = new This("Tom", 18);
test.show();
System.out.println("--------------");
This test2 = new This();
}
}
class This{
private String name;
private int age;
public This(){
//假设这个空参构造器要满足的条件很多 有40行代码 而且规定在造对象时必须满足这个条件
System.out.println("40行代码执行完毕!");
}
public This(String name){
this();//调用空参构造器满足要求
this.name = name;
}
public This(int age){
this();//调用空参构造器满足要求
this.age = age;
}
public This(String name, int age){
this(age);//调用带参构造器
this.name = name;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name = " + name + ", age = " + age);
}
}
综合练习见 package com.atguigu.oop1exer; BankTest
7.5 package、import关键字
7.5.1 package关键字
-
为了更好的实现项目中类的管理,提出包的概念,将不同功能的类分类
-
使用package声明类或接口所属的包,声明在源文件的首行
-
包,属于标识符,遵循标识符的命名规则和规范 ( 都是小写 ) ,见明知意
-
每 " . " 一次,就表示一层文件目录
补充 : 同一个包下不能命名同名的结构 (接口、类)
不同的包下可以命名同名的结构 (接口、类)
7.5.2 MVC设计模式
MVC是常用的设计模式之一,将整个程序分为 三个层次 : 视图模型层、控制器层与数据模型层。这种将程序输入输出、数据处理、以及数据的展示分离开来的设计模式使程序结构变得灵活且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
-
模型层 mdoel 主要处理数据
-
数据对象封装 model.bean / domain
-
数据库操作类 model.dao
-
数据库 model.db
-
-
视图层 view 显示数据
-
相关工具类 view.utils
-
自定义 view view.ui
-
-
控制层 controller 处理业务逻辑
-
应用界面相关 controller.activity
-
存放fragment controller.fragment
-
显示列表的适配器 controller.adapter
-
服务相关的 controller.serivce
-
抽取的基类 controller.base
-
7.5.3 import关键字
import : 导入
-
在源文件中显示的使用import结构导入指定包下的类或者接口
-
声明在包的声明和类的声明之间
-
多个import语句并列往下写
-
import java.util.* 表示导入 java.util 包下的所有类和接口
-
如果使用的类或接口是 java.lang包下定义的,则可以省略import
-
如果使用的类或接口是本包下定义的,则也可以省略import
-
如果在源文件中使用了不同包下的同名的类,则必须至少有一个类需要以全命名的方式进行显示
-
使用 "xxx.*" 方式表明可以调用xxx包下的所有结构。但是如果使用的是xxx子包下的结构,则仍需要显示导入
-
import static : 导入指定类或者接口中的静态结构 (属性或方法) 例如 : import static java.lang.System.out;
package com.atguigu.oop1;
import com.atguigu.test.Test;
/**
* @author hjc Email : jingchuan_Hou@163.com
* @data 2022-07-14-19:53
* @Description
* 在类中调用两个不同包下的同名的类,那么其中一个类需要以全命名的方式显示
* 另一个可以使用 import 的方式导入
*/
public class PackageImportTest {
public static void main(String[] args) {
//全命名的方式显示
com.atguigu.oop1exer.Test test1 = new com.atguigu.oop1exer.Test("dfs");
Test test2 = new Test(100);
}
}
package com.atguigu.oop1;
import static java.lang.System.*;//导入System类下的所有静态结构
//import static java.lang.System.out;导入System类下的静态属性out
import static java.lang.Math.round;//导入Math类下的静态方法round()
/**
* @author hjc Email : jingchuan_Hou@163.com
* @data 2022-07-14-19:53
* @Description
* 由于 java.lang.System 包下的 out 是一个静态结构 : public final static PrintStream out = null;
* 所以我们可以用 import static 将其导入,这样在使用的时候就不用加 System类了
*/
public class PackageImportTest {
public static void main(String[] args) {
out.println("hello world ! ");
long ret = round(123.12);
out.println(ret);
}
}