目录
C就是存面向过程编程的思想(C++是面向对象和面向过程的编程思想)
编程的两种思想_面向过程与面向对象
说明:
- 面向对象的思想可分为类、形容词、动作。举个例子:我想到一个好人,这个好人他可以孝敬父母、尊重长辈、关爱亲朋好友。好人中的“好”就是个形容词,好人中的“人”就是一个类,这个好人所能做的事是“孝敬父母、尊重长辈、关爱亲朋好友”,这些就是一个动作。
- 面向对象:Object Oriented Programming | 面向过程:Procedure Oriented Programming
- 参考下图简历了解面向对象
Java就存面向对象的编程思想
- 面向对象强调具有功能的对象,以类/对象为最小单位,考虑的是谁会做谁来做
- 一个Java类的成员有:属性、方法、构造器、代码块、内部类
- 面向对象三大特征:封装继承多态,(抽象性)
- 类中关键字:this、super、static、final、abstract、interface、package、import 等
C就是存面向过程编程的思想(C++是面向对象和面向过程的编程思想)
- 强调功能行为,以函数(方法)为最小单位,考虑的是怎么做
- 比如放大象到冰箱的过程就是:打开冰箱-->把大象放入冰箱-->关闭冰箱
面向过程到面向对象程序员的转变
- 程序员从面向过程的执行者转化成了面向对象的指挥者
- 面向对象分析方法分析问题的思路和步骤:
- 根据问题需要,选择问题所针对的现实世界中的实体。
- 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
- 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
- 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。
类和对象
说明:
- 面向对象两个要素:类和实例对象
- 类是对一类事物的抽象描述,具体的属性、方法还得具体情况具体设定
- 例如:人类有名字,并且能够跑
- 对象是一类事物的具体描述,不再是抽象的东西
- 例如:这个人类叫小明,他跑得很慢
- 面向对象最重要是类的设计,设计类其实就是设计类的成员(属性、方法、构造器、代码块、内部类)
类与对象的构建及使用
- 设计类就是设计类的成员(属性、方法、构造器、代码块、内部类)
- 创建类 = 实例化类
- 通过“对象.属性”或者“对象.方法”调用实体类(对象)的结构
- 区分实体类之间的区别就是每个对象拥有自己独立一套的类属性(非static,被static修饰后的属性属于类资源,存在方法区,像类一样只有一份)存在堆内存中拥有自己独立的空间,并且有专属的指向堆内存的地址
类的基本语法
修饰符 class 类名{
属性声明;
方法声明;
}
创建一个Person类
package javase6.pojo;
public class Person {
public String name;
public int age;
public boolean isMale;//是否为男性
public void eat(){
System.out.println("人可以吃");
};
public void sleep(){
System.out.println("人可以睡");
}
}
创建一个测试类来实例化Person类
package javase6;
import javase6.pojo.Person;
public class OOP1 {
public static void main(String[] args) {
Person person = new Person();
person.name = "小明";
person.age = 18;
person.isMale = true;
person.eat();//人可以吃
person.sleep();//人可以睡
}
}
对象的创建和使用:内存解析
JVM内存图解析:
图解:
- 堆(Heap),此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在 Java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
- 通常所说的栈(Stack),是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完,自动释放。
- 方法区(MethodArea),用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
对象的创建和使用流程简单描述:
- .java文件通过javac编译成.class文件,然后被java JVM虚拟机加载
- 类装载器将class文件中的类信息、常量、静态变量等数据加载到方法区
- 当我们new一个对象(new Person())时,会调用构造器创建实例对象存放到堆内存中
- 然后我们创建一个与对象符合的数据类型+变量(Person person)在虚拟机栈中,并使用这个变量存储指向堆内存的实例对象的内存地址.
- 当我们使用对象时,通过变量.属性或者变量.方法(),就能通过变量找到对应的实体类,去调用实体类.
Person p1= newPerson();
p1.name = "Tom";
p1.isMale = true;
Person p2 = new Person();
sysout(p2.name);//null
Person p3 = p1;
p3.age = 10;
类的成员之属性和方法(后面还有)
属性(成员变量)和局部变量
- 相同的:
- 定义变量格式都是: 数据类型 变量名 = 值;
- 先声明,后使用(回顾:声明就是 声明语法:<数据类型> <变量名称> = <值>;)
- 变量都有其对应的作用域,也就是在其所在的{}内有效
- 不同的:
- 在类中声明位置不同:
- 局部变量是在类的方法里、方法形参、构造器形参、构造器构造器内部(构造方法)的变量
- 成员变量是在类里与类的方法外
- 权限修饰符不一样:
- 修饰符权限范围图片参考
- 属性(成员变量)可以使用权限修饰符,默认不写就是使用default,但是不能写default修饰属性,会报错.
- 局部变量不可使用权限修饰符!!!!
- 对于 class 的权限修饰只可以用 public 和 default(缺省)。
- 默认初始化
- 属性声明时可以先声明后赋值,因为声明后都有默认的初始化值
- 声明语法:<数据类型> <变量名称>;
- 整型(byte、short、int、long):0
浮点型(float、double):0.0
字符型(char):0(或‘\u0000’) ##打印在控制台看到是空串,但是=="0"和"\u0000"
布尔型(boolean):false
引用数据类型(类、数组、接口):null
- 局部变量必须再声明和赋值在同一条代码上,因为局部变量没有初始化值
- 但在形参上,它只需声明不需要赋值
- 属性声明时可以先声明后赋值,因为声明后都有默认的初始化值
- 内存加载的位置不同
- 实例化对象时,属性在非static修饰时是存放在堆内存中
- 在调用方法时,局部变量会存放在虚拟机栈内存中
- 在类中声明位置不同:
创建一个User类
package javase6.pojo;
public class User {
//属性(或成员变量)
String name; //不加 private 即为缺省,也就是default,但是不能直接加,会报错
public int age; //不加 public 即为缺省
boolean isMale;
public void talk(String language){//language:形参,也是局部变量
System.out.println("我们使用" + language + "进行交流。");
}
public void eat(){
String food = "石头饼"; //石头饼:局部变量
System.out.println("北方人喜欢吃:" + food);
}
}
创建一个测试类
package javase6;
import javase6.pojo.User;
public class OOP2 {
public static void main(String[] args) {
User u1 = new User();
// System.out.println(u1.name);u1对象的name属性被default修饰,非同包下无权限访问
System.out.println(u1.age);
// System.out.println(u1.isMale);//与8行代码同理
u1.talk("俄语");
}
}
方法
-
类中方法的声明和使用
-
方法的声明语法:<权限修饰符> <返回值类型> <方法名>(形参列表){<方法体>}
-
注意:这里<>只是一个文本说明的符号,写代码不需要写上去,例如:public void main(String[] args){}
-
-
注意修饰符关键字"static"、"final"、"abstract".这里只做记忆,后面会详细谈到
-
-
在类中,方法的权限修饰符都是public修饰
-
如果方法体里面有"return"关键字,必须在方法声明时就要设置<返回值类型>为return后面的数据对应的数据类型,这就意味着你调用这个方法时,这个方法不仅会运行,还会响应给调用方法的方法返回一个设定的值
-
例如:public String returnString(){return "你好啊"}
-
例外:如果return后面没有跟值,也就是没有值返回,这里的return可以省略,返回值类型为void
-
-
如果方法体里没有"return"关键字,则需要在声明方法时就要设置<返回值类型>为void,这意味着你调用这个方法时,这个方法会运行,但不会响应给调用方法返回值
-
例如:public void emtryReturn(){方法体}
-
-
return关键字后不可声明执行语句,因为return会结束整个方法并且给调用方法返回值,因此写在后面的执行语句是永远不会执行的,因此会报错
-
方法名就是我们之前学习的"标识符",遵循标识符的规则和规范,做到方法名见名知其意
-
(形参列表)里面放方法声明.只有<数据类型 变量,数据类型变量 变量,......>的局部变量,这里可以得知形参列表里面可以有一个或者多个参数
-
方法参数的值传递机制就是浅复制,如果变量是基本数据类型,此时赋值的是变量所保存的数据值.如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
-
-
方法在类中,在不谈及类的static资源时,在实体类中可以调用实体类的属性,或者修改实体类的属性和调用其方法.
-
特殊点就是方法能调用方法 举例:类中的A方法调用类中A方法完成业务也是可以的.不过但容易出现堆内存溢出的错误"java.lang.StackOverflowError",因为方法调用本身就是一个死循环,在使用这种方法时需要在方法前限制条件,可以参考到时数组提及到的快速算法,此时就变成了递归方法.
-
-
方法不能声明别的方法,只能调用符合权限修饰符的方法
设置一个Customer类
package javase6.pojo;
public class Customer {
//属性
String name;
int age;
boolean isMale;
//形参列表为空的方法=,在调用该方法时不需要传参
public void eat() {
System.out.println("eat()函数被调用");
return;//如果return后面没有跟值,也就是没有值返回,这里的return可以省略,返回值类型为void
// System.out.println("hello");//return后不可以声明表达式,如下方法会报错
}
public void sleep(int hour) {//设置单个形参的方法
System.out.println("休息了" + hour + "个小时");
eat();//方法中可以调用方法,但是不可以声明方法
sleep(10);//可以调用自己,但容易出现JVM堆栈内存内存溢出的错误"java.lang.StackOverflowError",因为方法调用本身就是一个死循环
}
public void sleep(int hour, int min,int second){//设置多个形参的方法
System.out.println("休息了"+hour+"小时"+min+"分"+second+"秒");
}
//调用该方法时需要传入指定参数并且还会返回一个String类型的字符串,调用该方法的方法可以选择是否用变量接受还是取消;
public String getNation(String nation) {
String info = "我的国籍是:" + nation;
return info;
}
//声明异常
// public void info() {
//
// public void swim(){//错误的,方法里面不能声明方法,只能调用方法
//
// }
// }
}
设置测试类
package javase6;
import javase6.pojo.Customer;
public class OOP3 {
public static void main(String[] args) {
Customer c1 = new Customer();
c1.eat();//无参数方法调用,eat()函数被调用
// c1.sleep(6);//JVM堆栈内存内存溢出java.lang.StackOverflowError
c1.sleep(1, 1, 1);//休息了1小时1分1秒
String nation = c1.getNation("中国");//接受该方法的返回值
System.out.println(nation);//我的国籍是:中国
}
}
拓展练习——自定义数组工具类,顺便介绍文档注解其中一个作用
说明:
回顾之前提及的面向对象开发思想,就是只需要知道这个对象能做什么,再使用他做什么就可以了。这样的思想会让编程变得十分简单。不需要考虑具体这个对象是怎么做的,因为每个事都要自己考虑做的过程和逻辑是会加重负担的。这里我们整理我们之前的操作数组的各种方法到一个自定义类中,此后我们在当前所在的Java项目中,我们就可以使用这个公开类实例化后进行各种操作。
当然Java自己就有Arrays工具类,这里我们只设计Int数据类型数组的数组工具类。
自定义数组工具类
package javase6.pojo;
// /***/文档注解可以描述该方法该类是用来干什么的,使用的时候可以把鼠标放到方法名/类名就可以看到作者的描述
/**自定义的数组工具类*/
public class IntArrayUtils {
/**求数组最大值*/
public int getMax(int[] arr) {
int maxValue = arr[0];
for (int i = 1; i < arr.length; i++) {
if (maxValue < arr[i]) {
maxValue = arr[i];
}
}
return maxValue;
}
/**求数组最小值*/
public int getMin(int[] arr) {
int minValue = arr[0];
for (int i = 1; i < arr.length; i++) {
if (minValue > arr[i]) {
minValue = arr[i];
}
}
return minValue;
}
/**求数组总和*/
public int getSum(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
/**求数组平均值*/
public int getAvg(int[] arr) {
int avgValue = getSum(arr) / arr.length;
return avgValue;
}
/**反转数组*/
public void reverse(int[] arr) {
for (int i = 0; i < arr.length / 2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i - 1] = temp;
}
}
/**复制数组*/
public int[] copy(int[] arr) {
int[] arr1 = new int[arr.length];
for (int i = 0; i < arr1.length; i++) {
arr1[i] = arr[i];
}
return null;
}
/**数组排序,这里使用的是冒泡排序*/
public void sort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
/**遍历数组*/
public void print(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + ",");
}
System.out.println("]");
}
/**线性查找法查找指定元素*/
public int getIndex(int[] arr, int dest) {
for (int i = 0; i < arr.length; i++) {
if (dest==arr[i]) {
return i;
}
}
return -1;
}
}
测试类
package javase6;
import javase6.pojo.IntArrayUtils;
public class OOP6 {
public static void main(String[] args) {
IntArrayUtils util = new IntArrayUtils();
int[] arr = new int[]{32,5,26,74,0,96,14,-98,25};
int max = util.getMax(arr);
System.out.println("最大值为:" + max);//最大值为:96
System.out.print("排序前:");
util.print(arr);//排序前:[32,5,26,74,0,96,14,-98,25,]
util.sort(arr);
System.out.print("排序后:");
util.print(arr);//排序后:[-98,0,5,14,25,26,32,74,96,]
System.out.println("查找:");//查找:
int index = util.getIndex(arr, 5);
if(index > 0){
System.out.println("找到了,索引地址:" + index);//找到了,索引地址:2
}else{
System.out.println("没找到");
}
}
}
方法的重载(overload)
说明
- 同一个类中,允许存在一个以上的同名方法,只要他们参数个数不同、参数个数相同参数类型不同、参数个数相同参数循序不同也能构成方法重载。与方法的返回值类型、权限修饰符、形参变量名、方法体都无关。
- 在调用对象方法时就是通过传入什么参数来区分使用什么方法
1.判断:与void show(int a,char b,double c){}构成重载的有:
a)void show(int x,char y,double z){} // no
b)int show(int a,double c,char b){} // yes
c) void show(int a,double c,char b){} // yes
d) boolean show(int c,char b){} // yes
e) void show(double c){} // yes
f) double show(int x,char y,double z){} // no
g) void shows(){double c} // no
可变个数的形参
说明:
- JDK5.0新增特性
- 可变形参语法;<数据类>...<变量名>
- 当调用可变参数方法时,传入的参数个数是>=0个
- 同一个类中,可变个数形参的方法与本类中方法名相同,形参不同的方法之间可以构成方法重载
- 同一个类中,可变个数形参的方法与本类中方法名相同时,本类方法形参类型是数组且数据类型与可变个数形参方法相同,则不构成方法重载。即二者不可共存。因为你在使用这个参数的时候你可以把它理解成一个数组去遍历使用
- 可变个数形参在方法中的形参中声明时必须要在末尾。
- 也就意味着可变个数形参在方法中的形参中,最多只能声明一个可变形参。
package javase6;
public class OOP7 {
public static void main(String[] args) {
OOP7 oop7 = new OOP7();
oop7.show(1);//接受单个参数的show(int i)方法,参数为:1
oop7.show();//String类型参数什么都没传入
oop7.show("小明","小李","小张");//,第1传入来的参数是小明,第2传入来的参数是小李,第3传入来的参数是小张
oop7.show(1, "小红","小白","放牛的","飞行矮青瓜");//传入来的i1值为:1,第1传入来的参数是小红,第2传入来的参数是小白,第3传入来的参数是放牛的,第4传入来的参数是飞行矮青瓜
}
/**接受单个参数的show(int i)方法*/
public void show(int i){
System.out.println("接受单个参数的show(int i)方法,参数为:"+i);
}
/**不接受或接受一个或多个参数的show()方法,展示传入的参数*/
public void show(String...strs){
//把strs形参看做一个数组去遍历
int i = 1;
if(strs.length!=0) {
for (String str : strs) {
System.out.print(",第" + i + "传入来的参数是" + str);
i++;
}
System.out.println();
}else {
System.out.println("String类型参数什么都没传入");
}
}
/*同一个类中,可变个数形参的方法与本类中方法名相同时,本类方法形参类型是数组且数据类型与可变个数形参方法相同,
则不构成方法重载。即二者不可共存。因为你在使用这个参数的时候你可以把它理解成一个数组去遍历使用,
否则会抛出异常提示:java: 无法在javase6.OOP7中同时声明show(java.lang.String[])和show(java.lang.String...)*/
// public void show(String[] strs){
//
// }
//一个方法只能存在一个可变形参并且只能放在最后面
// public void show(int...ints,String...strs){}//错误写法
// public void show(String...strs,int i){}//错误写法
//正确的写法
public void show(int i1,String...strs){
//传入来的i1值
System.out.print("传入来的i1值为:"+i1);
//把strs形参看做一个数组去遍历
int i = 1;
if(strs.length!=0) {
for (String str : strs) {
System.out.print(",第" + i + "传入来的参数是" + str);
i++;
}
System.out.println();
}else {
System.out.println("String类型参数什么都没传入");//理论上这个代码是不会被执行的,因为和show(int i)方法冲突了
}
System.out.println();
}
}
递归方法的使用
package javase6;
public class OOP9 {
/*
* 递归方法的使用(了解)
* 1.递归方法:一个方法体内调用它自身。
* 2.方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
*
* 3.递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
*
*/
public static void main(String[] args) {
//已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),求f(10)的值
System.out.println(f(10));//10497
//输入一个数据n,计算斐波那契数列(Fibonacci)的第n个值,斐波那契数列就是当前数是前两个数之和 1 1 2 3 5 8 13 21 34 55
System.out.println(fibonacci(10));
}
//设计计算f(n+2)=2*f(n+1) + f(n)方法
static int f(int n) {
if (n == 0) {
return 1;
} else if (n == 1) {
return 4;
} else {
return 2 * f(n - 1) + f(n - 2);
}
}
//设计计算计算斐波那契数列方法
static int fibonacci(int n){
if(n==2 || n==1){
return 1;
}else {
return fibonacci(n-2)+fibonacci(n-1);
}
}
}
类的成员_方法 的拓展练习
练习1
package javase6;
public class OOP8 {
//需求:需要在method方法调用时只打印a=100,b=100
public static void main(String[] args) {
int a = 10, b = 10;//原有条件
method(a, b);//原有条件
System.out.println(a);//原有条件
System.out.println(b);//原有条件
}
//设计method代码
private static void method(int a, int b) {
a *= 10;
b *= 10;
System.out.println("a="+a);
System.out.println("b="+b);
System.exit(0);//正常终止当前运行的Java虚拟机,那么方法调用method方法,method方法执行完虚拟机就会关闭不会执行后面的其他方法
}
}
属性和方法小结
- Java语言中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构。
- 我们之前学习用Scanner类调用控制台,使用自己设定好的Student、person类然后调用它的属性和方法
- 后面学得到Java语言和前段html页面交互和后端数据库交互时,前后端结构在Java层面交互都体现了类和对象的作用
构造器(构造方法)
- 构造器的作用就是创建对象、初始化对象属性(成员变量).,因此一个类至少要有一个构造方法
- 构造方法语法(与方法的区别就是没有返回值,方法名一定是类名):<修饰符> <方法名>(形参列表){};
- 一个类至少要有一个构造方法,如果在类中没有写构造方法,默认生成一个我们看不见的无参构造
- 方法允许重载,意味着构造方法也可以重载
- 一旦写了构造方法,无参构造方法就不会生成,如需要则要手动写出来
package javase6;
public class OOP11 {
public static void main(String[] args) {
//创建实体对象
Person person1 = new Person();//我是无参构造方法
person1.eat();//多少岁?
Person person2 = new Person("小明");//null多少岁我都不知道呢!名字我也不知道
person2.eat();//多少岁?
Person person3 = new Person("小明",18);
person3.eat();//小明今年18岁,现在在吃饭
}
}
class Person{
//属性
String name;
int age;
/*构造器*/
// public Person(){}//如果不写任何构造方法默认就生成一个这样的构造器
//写无参构造方法,自定义它的方法体,每次new Person()实体化类时都会调用这个无参构造方法
public Person(){
System.out.println("我是无参构造方法");
}
//自定义含参构造方法,每次new Person(n)实体化类时都会调用这个含参构造方法
public Person(String n){
name = n;//如果没有局部变量,这里就是给实体类的成员变量赋值
}
//全参构造方法,也就是个实体类所有属性初始化并赋值,每次new Person(n,a)实体化类时都会调用这个圈参构造方法
public Person(String n,int a){
name = n;
age = a;
}
//方法
public void eat(){
if(age==0) {System.out.println("多少岁?");return;}
if(name==null) {System.out.println("叫什么名字?");return;}
System.out.println(name+"今年"+age+"岁,现在在吃饭");
}
}
JavaBean对象基本规范(属性,方法,构造器)
-
类是公共修饰符的(类只能被public 和 缺省public修饰)
-
方法时公共修饰符修饰的
-
属性赋值是用getter setter方法获取和赋予的
-
属性赋值的顺序是:new 创建实体类对象-->默认初始化-->显示初始化-->构造器中赋值-->通过setter方法给属性赋值,或者通过对象.属性赋值,规范是通过setter方法赋值
package javase6.pojo;
public class JavaBean {
private int id;
private String name;
public JavaBean() {
}
public void setId(int i) {
id = i;
}
public int getId() {
return id;
}
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
}
实际的规范是这样的 (后面学习到this作为补充)
package javase6.pojo;
public class JavaBean {
private int id;
private String name;
public JavaBean() {
}
// public void setId(int i) {
// id = i;
// }
//
// public int getId() {
// return id;
// }
//
// public void setName(String n) {
// name = n;
// }
//
// public String getName() {
// return name;
// }
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
UML 类图
上图补充:
- 表示 public 类型,-表示 private 类型,#表示 protected 类型
- 方法的写法: 方法的类型(+、-) 方法名(参数名:参数类型):返回值类型
关键字This的使用
- this 用来修饰、调用:属性、方法、构造器
- this 理解为:当前对象,或当前正在创建的对象。
- 在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性和方法。通常情况下,我们都选择省略“this.”。特殊情况下,如果方法的局部变量类的属性同名,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参
- 在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用正在创建的对象属性和方法.特殊情况下,如果构造器的局部变量类的属性同名,我们必须显式的使用"this.变量"的方式,表明此变量是属性,而非形参
- 我们可以在类的构造器中,显式的使用"this(形参列表)"的方式,调用本类中重载的其他的构造器,但只能调用一个,因为this(形参列表)"必须声明在类的构造器的首位
- 但是构造器中不能通过"this(形参列表)"的方式调用自己,或者多个构造器调用形成递归,会出现递归异常
this拓展练习
Account 类
package javase6.pojo;
public class Account {
private double balance;
public double getBalance() {
return balance;
}
public Account(double init_balance){
this.balance = init_balance;
}
//存钱操作
public void deposit(double amt){
if(amt > 0){
balance += amt;
System.out.println("存钱成功");
}
}
//取钱操作
public void withdraw(double amt){
if(balance >= amt){
balance -= amt;
System.out.println("取钱成功");
}else{
System.out.println("余额不足");
}
}
}
Customers类
package javase6.pojo;
public class Customers {
private String firstName;
private String lastName;
private Account account;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public Customers(String f, String l) {
this.firstName = f;
this.lastName = l;
}
}
Bank类
package javase6.pojo;
public class Bank {
private int numberOfCustomers; //记录客户的个数
private Customers[] customers; //存放多个客户的数组
public Bank(){
this.customers = new Customers[10];//创建一个长度为10的客户类型数组
}
//添加客户
public void addCustomers(String f,String l){
Customers cust = new Customers(f,l);
// customers[numberOfCustomers] = cust;
// numberOfCustomers++;
customers[numberOfCustomers++] = cust;
}
//获取客户的个数
public int getNumberOfCustomers() {
return numberOfCustomers;
}
//获取指定位置上的客户
public Customers getCustomers(int index) {
// return customers[index]; //可能报异常
if(index >= 0 && index < numberOfCustomers){
return customers[index];
}
return null;
}
}
测试类
package javase6;
import javase6.pojo.Account;
import javase6.pojo.Bank;
public class OOP13 {
public static void main(String[] args) {
Bank bank = new Bank();//新建一个银行,一个银行能容纳是个客户,默认客户数量为0
bank.addCustomers("Jane", "Smith");//银行添加一个用户
bank.getCustomers(0).setAccount(new Account(2000));//获取银行第一个用户,设置它的账户,为其新建一个账户初始金额为2000
bank.getCustomers(0).getAccount().withdraw(500);//获取银行第一个用户,获取账户,从中取出500,控制台打印"取钱成功"
double balance = bank.getCustomers(0).getAccount().getBalance();//获取银行,获取账号,查询金额接受返回值
//获取银行第一个用户,获取初始名,再从刚才获取该用户的金额一起打印到控制台
System.out.println("客户: " + bank.getCustomers(0).getFirstName() + "的账户余额为:" + balance);//客户: Jane的账户余额为:1500.0
System.out.println("***************************");
bank.addCustomers("万里", "杨");//为该银行再存入一个用户
System.out.println("银行客户的个数为: " + bank.getNumberOfCustomers());//银行客户的个数为: 2
}
}
(拓展)自己定义引用类型的数组
package javase6;
public class OOP4 {
public static void main(String[] args) {
Student[] stus= new Student[5];//数组动态初始化
stus[0] = new Student();//为数组赋值,对象初始化
System.out.println(stus[0].state);//1,表示修改成功
System.out.println(stus[1]);//null,引用类型数组初始化默认值为null
// System.out.println(stus[1].number);//异常,空指针异常java.lang.NullPointerException
stus[1] = new Student();//为数组赋值,对象初始化
System.out.println(stus[1].number);//0,对象初始化默认值根据类型定义默认值,因为number数据类型是int
}
}
//一个.java文件只允许有一个被public修饰的类,因此测试类OOP4使用public修饰,学生类不用public修饰
class Student{
int number;//学号
int state = 1;//年级
int score;//成绩
}
自己定义引用类型的数组的内存分析
匿名对象的使用
说明:
- 就是没有给予变量存储指向堆内存地址的实体类(对象),只使用一次,当用完时就会被JVM虚拟机的垃圾回收机制minor GC回收.
package javase6;
import javase6.pojo.Customer;
public class OOP5 {
public static void main(String[] args) {
//继续使用之前上的Customer类,以下的对象都是一次性的,用完就被舍弃了
String nation = new Customer().getNation("中国");
System.out.println(nation);//我的国籍是:中国
// new Customer().sleep(1);//堆栈内存溢出方法,不采用
new Customer().sleep(1, 1, 1);//休息了1小时1分1秒
new Customer().eat();//eat()函数被调用
}
}
面向对象特征之一:封装(后面还有)
说明:
- 封装一个类,我们只需要知道他能做什么就可以,不需要了解他是怎么做到的(再次强调).隐藏对象内部的复杂性,只对外部简单的接口
- 例如我们之前写的ArrayUtils类,里面有很多针对int类型数组的方法,我们只需要通过"类名.方法名"调用这些方法完成想要的事情就可以了,完全不需要考虑里面是怎么实现的.
- 实现程序高内聚,低耦合
- 高内聚:细节的东西都在类写好完成,外部不允许再干预
- 低耦合:外部只能通过遵循权限修饰符的情况下进行操作使用
- 便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想
之前问题引入说明
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里赋值操作只受到属性的数据类型和存储范围的制约。但除此之外,没有其他制约条件。
但是,实际问题中,我们往往需要给属性赋值加入额外限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行条件的添加。
我们需要避免用户再使用“对象.属性”的方式对属性进行赋值。则需要将属性声明为私有的(private),再给他设置赋值和获取值的getter、setter方法
package javase6;
public class OOP14 {
public static void main(String[] args) {
//通过类提供的接口创建对象
People p1 = People.getConstruction();
//通过类提供的接口设置对象属性
p1.setAge(18);
p1.setName("小明");
//通过类提供的接口过去对象属性
System.out.println(p1.getName()+"今年"+p1.getAge()+"岁了");//小明今年18岁了
}
}
class People{
//属性私有化,外界不能通过类名.属性来获取或者设置属性值,只能通过设置的getter setter方法获取或者设置
private String Name;
private int age;
//类值提供一个构造方法,而且还是私有化,只能通过下面的getConstruction创建本类对象
private People(){}
//获取和设置属性
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//设置获取对象的方法
public static People getConstruction(){
People p1 = new People();
return p1;
}
}
关键字:package、import 的使用
关键字——package
说明:
- 为了更好的实现项目中类的管理,提供包的概念
- 使用 package 声明类或接口(后面回学)所属的包,声明在源文件的首行,下一行可能是import引入包、再下行才是类或者接口
- 包,属于标识符,遵循标识符的命名规则全小写、并且要"见名知意"
- 每“.”一次,就代表一层文件目录。
- 同一个包下不允许有同名的接口或者同名的类,否则会报错,是不是public都不行
- 拥有包的权限修饰符有default缺省、protected、public
JDK主要包介绍
1.java.lang----包含一些 Java 语言的核心类,如 String、Math、Integer、System 和 Thread,提供常用功能
2.java.net----包含执行与网络相关的操作的类和接口。
3.java.io----包含能提供多种输入/输出功能的类。
4.java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
5.java.text----包含了一些 java 格式化相关的类
6.java.sql----包含了 java 进行 JDBC 数据库编程的相关类/接口
7.java.awt----包含了构成抽象窗口工具集(abstractwindowtoolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。B/S C/S
关键字——import
说明:
- import是导入的意思
- 在源文件中显式的使用import结构导入指定包下的类、接口
- 声明在包的声明和类的声明之间
- 如果需要导入多个结构,则并列写出即可
- 可以使用"xxx.*"的方式,表示可以导入xxx包下的所有结构。
- 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
- 如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类(不怎么用到)
- 如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入
- import static组合的使用:调用指定类或接口下的静态的属性或方法.
拓展练习
package javase6;
import java.util.Arrays;
import java.util.Random;
//练习四优化
public class OOP15 {
public static void main(String[] args) {
//创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
Students[] s = new Students[20];
Random random = new Random();
for (int i = 0; i < s.length; i++) {
s[i] = new Students();
s[i].setNumber(i + 1);
s[i].setScore(random.nextInt(101));//生成0-100分的整数,记住Java含头不含尾
s[i].setState(random.nextInt(3) + 1);//生成1-3的整数,记住Java含头不含尾
//打印出3年级(state值为3)的学生信息。
if (s[i].getState() == 3) {
System.out.println(s[i]/*.toString()*/);//该方法会默认调用Students类的toString()方法返回它的属性所有值,这里toString可以不写
}
}
//冒泡排序根据学生成绩由低到搞排序
Students agency = null;
for (int i = 1; i < s.length; i++) {
for (int j = 0; j < s.length-i; j++) {
if (s[j].getScore()>s[j+1].getScore()){
agency = s[j];
s[j]=s[j+1];
s[j+1]=agency;
}
}
}
//打印根据成绩由低到搞排序的学生成绩的学生数组
System.out.println(Arrays.toString(s));
}
}
//一个.java文件只允许有一个被public修饰的类,因此测试类OOP4使用public修饰,学生类不用public修饰
class Students {
//属性
private int number;//学号
private int state;//班级
private int score;//成绩
//设置编辑和获取属性的方法
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
//重写注解@Override,没有也可以,只是作为一个标识,体检涉及以下
@Override
public String toString() {
return "Students{" +
"number=" + number +
", state=" + state +
", score=" + score +
'}';
}
}
MVC设计模式
说明:
MVC 是常用的设计模式之一,将整个程序分为三个层次:视图模型层,控制器层,数据模型层。这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性
个人人为这里解释还是有点难度