java——面向对象(上)
目录
- java——面向对象(上)
- 面向对象的思想概述
- 类的成员(1-2):属性和方法
- 对象的内存解析
- 类中属性的使用
- 类中方法的使用
- 1.举例:
- 2.声明方法:
- 3.说明
- 4.return关键字的使用:
- 5.方法的使用中,可以调用当前类的属性或方法
- 实例1:求圆的面积
- 实例2:打印矩形
- 实例3:打印学生信息
- 实例4:对实例三的改进
- 面向对象的知识点总结
- 万事万物皆对象
- 对象数组的内存解析
- 匿名对象的使用
- 自定义数组的工具类
- 再谈方法:方法的重载
- 再谈方法:可变个数的形参
- 再谈方法:方法参数的值传递机制
- 变量的赋值
- 方法形参的值传递机制
- 再谈方法:递归方法的使用
- 实例1:求和
- 实例2:求阶乘
- 实例3:应用
- 实例4:斐波那契数列
- 阶段性总结
- 面向对象特征一:封装与隐藏
- 封装性的引入
- 封装性的体现
- 四种权限修饰符
- 类的成员(3):构造器
- 构造器的作用:
- 说明
- 实例:三角形
- 总结:属性赋值的先后属性
- javaBean
- 关键字:this的使用
- 关键字:package的使用
- 关键字:import的使用
- MVC设计模式
java是一门面向对象的语言
1.java类及类的成员:属性、方法、构造器;代码块、内部类
2.面向对象的三大特征:封装性、继承性、多态性、(抽象性)
3.其它关键字:this、super、static、final、abstract、interface、package、import
面向过程&面向对象
面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做
面向对象:强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
面向对象的思想概述
面向对象的两个要素
**类(Class)**是对一类事物的描述,是抽象的,概念上的定义
**对象(Object)**是实际存在的该类事物的每个个体,因此也称为实例(instance)
设计类就是设计类的成员
类的成员(1-2):属性和方法
常见的类成员:属性、行为(方法)
属性:成员变量------>field/域、字段
方法:成员方法------>函数(C语言)------>method
三步骤:
step1:创建类,设计类的成员
step2:创建类的对象
step3:通过“对象.属性”或“对象.方法”调用对象的结构
class Person{
//属性
String name;
int age;
boolean isMale;
//方法
public void eat() {
System.out.println("人可以吃饭");
}
public void sleep() {
System.out.println("人可以睡觉");
}
public void talk(String language) {
System.out.println("人可以说话,使用的是"+language);
}
}
当我们创建一个类后,我们怎么使用呢——对象:java的实例化
/*类和对象的使用(面向对象思想落地的实现)
三步骤:
step1:创建类,设计类的成员
step2:创建类的对象
step3:通过“对象.属性”或“对象.方法”调用对象的结构
*/
public class personTest {
public static void main(String[] args) {
//创建person类的对象
//step2:创建类的对象
Person p1=new Person();
//Scanner scanner=new Scanner(System.in)
//调用对象的结构:属性、方法
//step3:通过“对象.属性”或“对象.方法”调用对象的结构
//调用属性:“对象.属性”
p1.name="Tom";
p1.isMale=true;
System.out.println(p1.name);
//调用方法:“对象.方法”
p1.eat();
p1.sleep();
p1.talk("Chinese");
}
}
//step1:创建类,设计类的成员
class Person{
//属性
String name;
int age;
boolean isMale;
//方法
public void eat() {
System.out.println("人可以吃饭");
}
public void sleep() {
System.out.println("人可以睡觉");
}
public void talk(String language) {
System.out.println("人可以说话,使用的是"+language);
}
}
现在我们再创建一个对象
Person p2=new Person();
System.out.println(p2.name);//Tom? null?
System.out.println(p2.isMale);//false
Person p3=p1; //将p1变量保存的对象地址赋值给p3,导致p1和p3指向了堆空间中同一个对象实体
System.out.println(p3.name);//Tom
结果为null,这是什么意思呢?
当我们new了之后,我们就在堆空间申请一片内存空间:当我们创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性(非static);这就意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值
对象的内存解析
类中属性的使用
属性(成员变量)vs局部变量
1.相同点
1.1定义变量的格式:数据类型 变量名=变量值
1.2先声明、后使用
1.3变量都有其对应的作用域
2.不同点
2.1在类中声明的位置不同
属性:直接定义在类的一对{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
2.2关于修饰符的不同
属性:可以在声明属性时,指明其权限,使用权限修饰符
常用的权限修饰符:private、public、缺省、protected—>封装性
目前,大家声明属性时,都使用缺省就可以了
局部变量:不可以使用权限修饰符
2.3默认初始化值的情况:
属性:类的属性根据其类型都有默认初始化值(类比一维数组元素的初始化值)
整型(byte、short、int、long):0
浮点型(float、double):0.0
字符型(char):0
布尔型(boolean):false
引用数据类型(类、数组、接口):null
局部变量:没有默认初始化值
意味着我们调用局部变量前,一定要赋值
特别的,形参在调用时,再赋值即可
2.4在内存中加载的位置:
属性:加载到堆空间中(非static)
局部变量:加载到栈空间
package Class_and_Object;
public class UserTest {
public static void main(String[] args) {
User u1=new User();
//下面查看属性的初始化值
System.out.println(u1.name);
System.out.println(u1.age);
System.out.println(u1.isMale);
}
}
class User{
//属性
String name;
public int age;
boolean isMale;
//方法
public void talk(String language) {
//language是局部变量;这里可以在调用的时候赋值(因为是形参)
System.out.println("我们使用"+language+"进行交流");
}
public void eat() {
String food="米饭";//局部变量
System.out.println("我们的主食是"+food);
}
}
类中方法的使用
方法:描述类应该具有的功能
比如:Math类:sqrt() \random()…
1.举例:
class Customer{
//属性
String name;
int age;
boolean isMale;
//方法
public void eat() {
System.out.println("客户吃饭");
}
public void sleep(int hour) {
System.out.println("休息了:"+hour+"小时");
}
public String getName() {
return name;
}
public String getNation(String nation) {
String information="我的国籍是:"+nation;
return information;
}
}
这里运用到的方法为:
public void eat()
public void sleep(int hour)
public String getName()
public String getNation(String nation)
2.声明方法:
格式:
权限修饰符 返回值类型 方法名(形参列表){ //形参列表根据实际情况不止一个
方法体;
}
3.说明
**3.1关于权限修饰符:**默认方法的权限修饰符先都使用public
java规定的四种权限修饰符:private、public、缺省、protected ------>封装性
**3.2返回值类型:**有返回值 vs 没有返回值
3.2.1如果方法有返回值:则必须在方法声明时。指定返回值的类型;同时方法中需要用return关键字来返回返回指定类型的变量或常量;
如果方法没有返回值:则在方法声明时,使用void来表示,通常,没有返回值的方法中,就不用使用return;但是,如果使用return的话,表示结束此方法的意思
3.2.2我们定义方法的时候该不该有返回值?经验主义
**3.3方法名:**属于标识符(见名知意)
**3.4形参列表:**方法可以声明0个、1个、多个形参
格式:数据类型1 形参1,数据类型2 形参2,......
4.return关键字的使用:
(1.使用范围,使用在方法体中
(2.作用:结束方法;
针对有返回值类型的方法,使用“return 数据”方法返回所需要的数据
(3.注意点:return关键字后面不可以声明执行语句
5.方法的使用中,可以调用当前类的属性或方法
【特殊的】,方法A中又调用了方法A:递归方法
方法中,不可以定义方法
实例1:求圆的面积
public class CircleTest {
public static void main(String[] args) {
Circle c1=new Circle();
c1.radius=2;
double area=c1.findArea();
System.out.println(area);
}
}
class Circle{
//属性
double radius; //半径
//求圆的面积
public double findArea() {
double area=3.14*radius*radius;
return area;
}
}
public class Print_Matrix_class {
public static void main(String[] args) {
Print_Matrix_class test = new Print_Matrix_class();
test.print_methed();
}
//编写一个能打印*矩形的方法
public void print_methed() {
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 8; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
注意括号的包含关系 print_methed()这个方法是包含在public class 类当中的,相当于是整个文件的大类,因此我们在使用方法时仍然需要创建实例化对象:Print_Matrix_class test = new Print_Matrix_class();其中的test就是我们所创造的对象
实例2:打印矩形
public class Print_Matrix_class {
public static void main(String[] args) {
Print_Matrix_class test = new Print_Matrix_class();
int area=test.print_methed();
//方式1:
System.out.println("面积为:"+area);
//方式2:
System.out.println(test.print_methed());
}
//编写一个能打印*矩阵的方法
public int print_methed() {
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 8; j++) {
System.out.print("*");
}
System.out.println();
}
//System.out.println(10*8);
return 10*8;
}
}
System.out.println(test.print_methed());
对于这段语句,输出语句也可以在括号内使用【方法】,这里将方法看作成了一个变量,变量的值即是该方法的返回值(该类调用方法不会报错)
public class Print_Rectangle_class02 {
public static void main(String[] args) {
Print_Rectangle_class02 test = new Print_Rectangle_class02();
int result=test.method(5, 5);
//方法1:
System.out.println(result);
//方法2:
System.out.println(test.method(5, 5));
}
public int method(int m, int n) {
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
System.out.print("*");
}
System.out.println();
}
return m*n;
}
}
实例3:打印学生信息
public class Students_class {
public static void main(String[] args) {
// 声明20个对象
// student s1=new student(); //太多了
// 声明student类型的数组
student[] stu = new student[20]; // 对象数组
for (int i = 0; i < 20; i++) {
// 给数组元素赋值
stu[i] = new student();
// 给student对象属性赋值
stu[i].number = i + 1;
// 年级在1-6之间取随机数
stu[i].state = (int) (Math.random() * (6 - 1 + 1) + 1);
// 成绩在0-100范围
stu[i].score = (int) (Math.random() * (100 - 0 + 1) + 1);
}
// 遍历学生数组
for (int i = 0; i < stu.length; i++) {
// System.out.println(stu[i]); 输出的结果为地址
// 对于这类引用类型的变量,不是null那么就存地址
// 这里因为stu已经new了,那么它就已经不是null了
// 如果我们需要打印出值,我们需要引用它
System.out.println(stu[i].number + "," + stu[i].state + "," +stu[i].score);
}
// 使用方法来实现
// 方法1:
for (int j = 0; j < stu.length; j++) {
String info = stu[j].Studentinfo();
System.out.println(info);
}
// 方法2:
for (int j = 0; j < stu.length; j++) {
System.out.println(stu[j].Studentinfo());
}
//打印出state3年级学生的成绩
for (int j = 0; j < stu.length; j++) {
if(stu[j].state==3) {
System.out.println(stu[j].Studentinfo());
}
}
//使用冒泡排序队学生成绩排序
for(int i=0;i<stu.length-1;i++) { //控制每一大轮
for(int j=0;j<stu.length-1-i;j++) { //控制每一轮中的交换
if(stu[j].score>stu[j+1].score) {
student temp=stu[j];
stu[j]=stu[j+1];
stu[j+1]=temp;
}
}
}
for (int i = 0; i < stu.length; i++) {
System.out.println(stu[i].number + "," + stu[i].state + "," +stu[i].score);
}
}
}
class student {
// 属性
int number; // 学号
int state; // 年级
int score; // 成绩
// 方法
// 显示学生信息
public String Studentinfo() {
return "学号" + number + "年级" + state + "成绩" + score;
}
}
【注意】:在冒泡排序中我们需要注意的是,我们定义临时变量时不要忘记变量类型是student
student temp=stu[j];
然后就是在交换时,错误的写法是:
student temp.score=stu[j];
stu[j].score=stu[j+1].score;
stu[j+1].score=temp;
因为如果只对score进行交换那么该学生的成绩即是别人的成绩了,因此在交换时要交换的是这个学生的整个属性
实例4:对实例三的改进
将操作数组的功能封装到方法中
public class Students_class02 {
public static void main(String[] args) {
// 声明20个对象
// student s1=new student(); //太多了
// 声明student类型的数组
student1[] stu = new student1[20]; // 对象数组
for (int i = 0; i < 20; i++) {
// 给数组元素赋值
stu[i] = new student1();
// 给student对象属性赋值
stu[i].number = i + 1;
// 年级在1-6之间取随机数
stu[i].state = (int) (Math.random() * (6 - 1 + 1) + 1);
// 成绩在0-100范围
stu[i].score = (int) (Math.random() * (100 - 0 + 1) + 1);
}
Students_class02 test=new Students_class02 (); //在Students_class02 中造了方法,我们在使用前需要new一个对象
test.traverse_print(stu);
test.searchState(stu,6);
test.sort(stu);
}
//遍历
public void traverse_print(student1[] stu) {
for (int i = 0; i < stu.length; i++) {
System.out.println(stu[i].number + "," + stu[i].state + "," + stu[i].score);
}
}
//选择年级
public void searchState(student1[] stu, int state) {
for (int j = 0; j < stu.length; j++) {
if (stu[j].state == state) {
System.out.println(stu[j].Studentinfo());
}
}
}
//按成绩排序
public void sort(student1[] stu) {
for (int i = 0; i < stu.length - 1; i++) { // 控制每一大轮
for (int j = 0; j < stu.length - 1 - i; j++) { // 控制每一轮中的交换
if (stu[j].score > stu[j + 1].score) {
student1 temp = stu[j];
stu[j] = stu[j + 1];
stu[j + 1] = temp;
}
}
}
}
}
class student1 {
// 属性
int number; // 学号
int state; // 年级
int score; // 成绩
// 方法
// 显示学生信息
public String Studentinfo() {
return "学号" + number + "年级" + state + "成绩" + score;
}
}
面向对象的知识点总结
1.面向对象思想编程内容的三条主线是什么
a.类及类的成员:属性、方法、构造器、代码块、内部类
b.面向对象的三大特征:封装、继承、多态
c.其它关键字:this,super,abstract,interface,static,final,package,import
面向对象的编程思想?
2.面向对象中类和对象的理解:对象是类的实例化(对象是由类new出来的)
类:抽象的、概念上的内容
对象:实实在在存在的一个个体(在内存中真正占据空间)
3.类和对象的创建和执行操作的三步
创建类------>类的实例化------>调用对象的结构:“对象.属性” “对象.方法”
4.内存分配情况
5.面向过程:功能行为,以函数为最小单位
面向对象:强调了具备了功能的对象
6.内存解析说明
preson p1=new person();
preson p2=new person();
preson p3=p1; //没有新建一个对象,共用一个堆空间
JVM(java虚拟机)内存结构
编译完源程序以后,生成一个或多个字节码文件,我们使用JVM类的加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析
我们将new出来的结构(比如:数组、对象)加载到空间中。补充:对象的属性(非static的)加载到堆空间中
万事万物皆对象
1.在Java语言范畴中,我们都将功能结构等封装到类中,通过类的实例化,来调用具体的功能结构
2.涉及到java语言与前端HTML、后端数据库交互时,前后端结构在java层面交互时,都体现为类、对象
Scanner,String等
文件、File
网络资源,URL
对象数组的内存解析
匿名对象的使用
代码引入:
public class Anonymous_Objects {
public static void main(String[] args) {
Phone p=new Phone();
//p=null;
System.out.println(p); //带类型的地址
p.playGame();
p.sendEmail(); //有名字的对象:p(对象名)
//匿名对象
new Phone().sendEmail();
new Phone().playGame(); //这两个用的就不是一个对象了(每new一个就是一个新对象)
new Phone().price=1999;
new Phone().showPrice(); //0.0(double类型,再次说明不是同一对象了)
}
}
class Phone{
double price;
public void sendEmail() {
System.out.println("发送邮件");
}
public void playGame() {
System.out.println("玩游戏");
}
public void showPrice() {
System.out.println(price);
}
}
理解:
1.我们创建的对象,没有显示的赋给一个变量名,即为匿名对象
2.特征:匿名对象只能调用一次
3.使用:如下
class PhoneMall{
public void showPhone(Phone iphone) { //
iphone.sendEmail();
iphone.playGame();
}
}
PhoneMall p2=new PhoneMall();
p2.showPhone(new Phone());//匿名对象(new一个Phone类型的对象,只使用这一次)
自定义数组的工具类
通过上述的代码,我们体会到了将具体的思路封装到方法中,在我们使用的时候只需要调用这个方法即可,给我们程序带来了极大的方便,因此我们可以试着将数组的一系列方法也给包装到方法中去
//自定义数组工具类
public class Custom_Array_Tool_Class {
// 求数组的最大值
public int getMax(int[] arr) {
int maxValue = arr[0];
for (int i = 0; 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 = 0; 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 double getAverage(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
double averageValue = (sum * 1.0) / arr.length;
return averageValue;
}
// 反转数组
public void flipsArray(int[] arr) {
for (int i = 0; i < arr.length / 2; i++) {
int temp = arr[i];
arr[i] = arr[arr.length - 1 - i];// 这里需要注意数组下标为arr.length-1代表的是最后一个元素
arr[arr.length - 1 - i] = temp;
}
}
// 复制数组
public void copyArray(int[] arr) {
int[] arr_copy = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
arr_copy[i] = arr[i];
}
}
// 数组排序
public void sortArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] < arr[j + 1]) {
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
}
// 遍历数组
public void throughArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
}
// 查找指定元素(线性查找)
public int searchElement(int[] arr, int target) {
for (int i = 0; i < arr.length; i++) { //这样就不用像之前那样再定义布尔变量啥的了
if (target == arr[i]) {
return i;
}
}
return -1;//返回负数代表没找到
}
}
测试:(使用)
//使用我们自定义的数组工具Custom_Array_Tool_Class
public class Use_My_Arraytools {
public static void main(String[] args) {
Custom_Array_Tool_Class tool=new Custom_Array_Tool_Class(); //对象是类的实例化
int[]arr=new int[]{32,45,67,44,-11,22,78,100,-56,-99,28};
int max=tool.getMax(arr);
System.out.println("最大值为:"+max);
int min=tool.getMin(arr);
System.out.println("最小值为:"+min);
//等等
}
}
再谈方法:方法的重载
1.概念:在同一个类中,允许在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可
两同一不同:同一个类、相同方法名
参数列表不同;参数个数不同;参数类型不同
2.举例:Arrays类中重载的sort()/binarySearch()
调用System.out.println()方法就是经典的方法的重载
3.判断是否重载:
跟方法的权限修饰符、返回值类型、形参变量名、方法都没有关系
4.在通过对象调用方法时,如何确定某一个指定方法:
方法名------>参数列表
再谈方法:可变个数的形参
1.jdk5.0新增的内容
2.具体使用
2.1格式
public void show(String ... strs){
System.outy.println("show(String ... strs)");
}
2.2当调用可变个数形参方法时,传入的参数个数可以是0个、1个、2个…
2.3可变个数形参的方法与本类中方法名相同,形参不同的方法直接构成重载
2.4可变个数形参的方法与本来中方法名相同,形参类型也相同的数组之间不构成重构(换句话说:二者不能共存)
意思是两者可以相互的取代
public void show(String ... strs){
System.outy.println("show(String ... strs)");
}
public void show(String[] strs){
System.outy.println("show(String ... strs)");
}
并且在调用时两者也可以相互的表示
test.show(new String[]{"AA","BB","CC"});
test.show("AA","BB","CC");
因此我们可以理解为:系统对于可变个数的形参,仍然是把它当作一个数组,但是相比数组来说书写更简单
2.5可变个数形参在方法的形参中,必须声明在末尾
public void show(int i,String ... strs); //right
public void show(String ... strs,int i); //wrong
2.6可变个数形参在方法的形参中,最多只能声明一个可变形参
再谈方法:方法参数的值传递机制
变量的赋值
public class Value_of_Method {
public static void main(String[] args) {
// 对于引用数据类型的值传递
Order o1 = new Order();
o1.OrderId = 1;
Order o2 = o1;
System.out.println("o1.OrderId=" + o1.OrderId + "\t" + "o2.OrderId=" + o2.OrderId);
o2.OrderId = 2;
System.out.println("o1.OrderId=" + o1.OrderId + "\t" + "o2.OrderId=" + o2.OrderId);
}
}
class Order {
int OrderId;
}
运行结果如下:
分析:赋值以后,对于引用数据变量类型来说,o1和o2的地址值相同,都指向了堆空间中同一个对象实体
关于变量的赋值:
1.如果变量是基本数据类型,此时赋值的是变量所保存的数据值
2.如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
方法形参的值传递机制
1.形参:方法定义时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的数据
2.值传递机制
引入:
public class Value_Transfer02 {
public static void main(String[] args) {
int m=10;
int n=20;
System.out.println("m="+m+",n="+n);
//交换两个变量
//int temp=m;
//m=n;
//n=temp;
//交换的操作很常用,我们不妨将此封装到方法中
Value_Transfer02 useSwap=new Value_Transfer02();
useSwap.Swap(m,n);
System.out.println("m="+m+",n="+n);
}
public void Swap(int m,int n){
int temp=m;
m=n;
n=temp;
}
}
运行结果如下:
我们发现并未达到我们的交换的目的!
通过内存来理解:
造成原因:如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值
因此:如果变量是引用数据类型,此时赋值的变量所保存的数据的地址值
运用到数组上:
public class Arraysorting_class {
public static void main(String[] args) {
int[] arr=new int[] {-12,22,45,-87,-22,56,78,23,-66};
Arraysorting_class arrnum=new Arraysorting_class();
for(int i=0;i<arr.length;i++) {
for(int j=0;j<arr.length-1-i;j++) {
if(arr[j]<arr[j+1]) {
arrnum.Swap(arr, i, j);
}
}
}
for(int i=0;i<arr.length;i++) {
System.out.print(arr[i]+" ");
}
}
public void Swap(int[] arr,int i,int j) {
int tem=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tem;
}
}
【总结】:值传递机制
如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值
如果变量是引用数据类型,此时赋值的变量所保存的数据的地址值
实例:
public class UseClass_Circle {
//main方法
public static void main(String[] args) {
PassObject Use=new PassObject();
Circle c=new Circle();
Use.printAreas(c, 5);
System.out.println("now radius is:"+c.radius);
}
}
//输出半径和对应的圆面积
public class PassObject {
public void printAreas(Circle c, int time) {//在这里已经定义了一个Circle类型的形参c了
System.out.println("Radius\t\tArea");
for (int i = 1; i <= time; i++) {
c.radius = i;
// 不用匿名对象
double area = c.findArea();
System.out.println(c.radius + "\t\t" + area);
// 用匿名对象
// System.out.println(c.radius + "\t\t" + c.findArea());
}
c.radius = time + 1;
}
}
//Circle类
class Circle {
double radius;
public double findArea() {
return 3.14 * radius * radius;
}
}
再谈方法:递归方法的使用
1.递归方法:一个方法体内调用它自身
2.方法的递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无需控制循环;递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
实例1:求和
public class Recursion_Test {
public static void main(String[] args) {
//实例1:计算1-100所有自然数的和
//循环
int sum=0;
for(int i=1;i<=100;i++) {
sum+=i;
}
System.out.println(sum);
Recursion_Test Test=new Recursion_Test();
int Result=Test.getSum(100);
System.out.println(Result);
//System.out.println(Test.getSum(100));//匿名对象
}
//递归方法
public int getSum(int n){//前n个数求和等于前n-1个数求和加上第n个数
if(n==1) {
return 1;
}
else {
return n+getSum(n-1);
}
}
}
同理的,我们可以写出n的阶乘的计算
实例2:求阶乘
public int getFactorial(int n){
if(n==1) {
return 1;
}
else {
return n*getFactorial(n)
}
}
}
实例3:应用
已知有一个数组:f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值
//已知有一个数组:f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值
public class Recursion_Test2 {
public static void main(String[] args) {
Recursion_Test2 Use = new Recursion_Test2();
int Result = Use.getNum(10);
System.out.println(Result);
}
public int getNum(int n) {
if (n == 0) {
return 1;
} else if (n == 1) {
return 4;
} else {
return 2 * getNum(n - 1) + getNum(n - 2);
// f(n+2)=2*f(n+1)+f(n)--->f(n)=2*f(n-1)+f(n-2)
}
}
}
实例4:斐波那契数列
//斐波那契数列
public class Recursion_Test3 {
public static void main(String[] args) {
Recursion_Test3 Use=new Recursion_Test3();
System.out.println(Use.Fibonacci(10));
}
public int Fibonacci(int n) {
if(n==1) {
return 1;
}
else if(n==2) {
return 1;
}
else {
return Fibonacci(n-1)+Fibonacci(n-2);
}
}
}
阶段性总结
1.什么是方法的重载?
两同一不同:同一个类、相同方法名
参数列表不同;参数个数不同;参数类型不同
------>如何调用一种确定的方法:方法名------>参数列表
方法的重载和重写的区别?:没什么关系
2.java方式中参数传递机制的继续体现
如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值
如果变量是引用数据类型,此时赋值的变量所保存的数据的地址值(含变量的数据类型)
3.成员变量和局部变量
4.return关键字的使用:结束方法、针对于有返回值的方法:return+返回数据
5.内存结构:栈(局部变量)、堆(new出来的结构:对象(成员变量)、数组)
面向对象特征一:封装与隐藏
封装性的引入
我们程序设计追求:“高内聚,低耦合”
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
低耦合:仅对外暴露少量的方法用于使用
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想
引入:当我们创建一个类的对象后,我们可以通过“对象.属性”的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没有其它制约条件;但是在实际问题中,我们往往要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如,setLegs,同时我们要避免用户再使用"对象.属性“的方式对属性进行赋值。则需要将属性声明为私有的(private),此时针对于属性就体现了封装性。
public class Animal_Test {
public static void main(String[] args) {
Animal a=new Animal();
a.name="Peter";
a.age=2;
a.setLegs(4);
a.show();
//a.legs=-4; //虽然我们设计了方法setLegs能对输入的不合法数据进行辨析,但是我们 仍然能通过访问属性来修改legs的值
//因此我们将属性legs设为一个私有属性:private,就不能在修改了
}
}
class Animal{
String name;
int age;
private int legs; //腿的个数
public void eat() {
System.out.println("动物吃东西");
}
public void show() {
System.out.println("name="+name+",age="+age+",legs="+legs);
}
//对属性的设置
public void setLegs(int l) {
if(l>=0&&l%2==0) {
legs=l;
}
else {
legs=0;
}
}
//对属性的获取
public int getLegs() {
return legs;
}
}
封装性的体现
我们将类的属性私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx);这样的话:
a.legs=4; //wrong
四种权限修饰符
封装性的体现,需要权限修饰符来配合
1.java规定的四种权限(从小到大排列):
private、缺省、protected、public
缺省的意思就是什么都没有写
2.四种权限可以修饰类及类的内部结构:属性、方法、构造器、内部类
3.具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能用:缺省、public
总结封装性:java提供了四种权限修饰来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小
类的成员(3):构造器
任何一个类都有构造器(Constructor)
类的前两个成员:属性和方法
构造器的作用:
1.创建对象(new对象时new接的都是构造器)
2.初始化对象的属性
//创建对象
public class PersonTest {
public static void main(String[] args) {
//创建类的对象:new+构造器
Person p=new Person(); //Person():构造器
p.eat();
}
}
class Person{
//属性
String name;
int age;
//方法
public void eat() {
System.out.println("人可以吃饭");
}
public void sleep() {
System.out.println("人可以睡觉");
}
}
说明
1.如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
2.定义构造器的格式:
权限修饰符 类名(形参列表){ //构造器名与类同名
}
class Person{
//属性
String name;
int age;
//构造器
//作用1:创建对象
public Person() {
System.out.println("已被调用");
}
//作用2:给对象进行初始化
public Person(String s){
name=s;
}
//方法
public void eat() {
System.out.println("人可以吃饭");
}
public void sleep() {
System.out.println("人可以睡觉");
}
}
3.一个类种定义多个构造器,彼此构成重载
4.一旦我们显示定义了类的构造器之后,系统就不再提供默认的空参构造器
5.一个类中,至少会有一个构造器(默认/自己写的)
实例:三角形
public class TriangleTest {
public static void main(String[] args) {
Triangle t1=new Triangle();
t1.setBase(2.0);
t1.setHight(2.4);
//t1.base=2.5; wrong:private(存在但不可见)
System.out.println("base:"+t1.getBase()+",hight:"+t1.getHight());
//通过构造器的初始化
Triangle t2=new Triangle(4.0,2.5);
System.out.println("base:"+t2.getBase()+",hight:"+t2.getHight());
}
}
public class Triangle {
private double base; //底
private double hight; //高
//构造器
public Triangle() {
//空参
}
public Triangle(double b,double h) {
base=b;
hight=h;
}
public void setBase(double b) {
base=b;
}
public double getBase() {
return base;
}
public void setHight(double h) {
hight=h;
}
public double getHight() {
return hight;
}
}
总结:属性赋值的先后属性
1.默认初始化
2.显式初始化
3.构造器中赋值
4.通过“对象.方法”(封装性)或“对象.属性”赋值
以上操作的先后顺序: 1------>2------>3------>4
javaBean
javaBean是一种Java语言写成的可重用组件
所谓javaBean,是指符合如下标准的java类:
类是公开的
一个无参的公共的构造器
有属性,且有对应的get、set方法
//javaBean
public class customer2 { //类是公开的
private int id;
private String name;
public customer2() {
//一个无参的公共的构造器
}
//有属性,且有对应的get、set方法
public void setId(int i) {
id = i;
}
public int getId() {
return id;
}
public void setName(String s) {
name = s;
}
public String getname() {
return name;
}
}
关键字:this的使用
this关键字的使用:
1.this可以用来修饰:属性、方法、构造器
2.this理解为:当前对象
2.1在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,通常情况下,我们都选择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表面此变量是属性,而非形参
2.2在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,通常情况下,我们都选择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式的使用"this.变量"的方式,表面此变量是属性,而非形参
实例:account
public class Account {
//属性
private int id; //账号
private double balance; //余额
private double annualInterestRate; //年利率
//构造器
public Account(int id,double balance,double annualInterestRate) {
this.id=id;
this.balance=balance;
this.annualInterestRate=annualInterestRate;
}
//get/set方法:Source
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
//取钱
public void withdraw(double amount) {
if(balance<amount) {
System.out.println("余额不足");
}
else {
System.out.println("成功取出"+amount+"元");
}
}
public void deposit(double amount) {
if(amount>0) {
balance+=amount;
System.out.println("成功存入:"+amount+"元");
}
}
}
public class Customer {
private String firstName;
private String lastName;
private Account account; //在属性中出现自定义类型的变量(引用数据类型)
//构造器
public Customer(String f,String l) {
this.firstName=f;
this.lastName=l;
}
//get-set
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
public class Test {
public static void main(String[] args) {
Customer c=new Customer("peter","smith"); //先实例化一个客户
Account a=new Account(1000, 2000, 0.0123); //再为它创建一个账户
c.setAccount(a); //这样这个账户就是这个客户的了
//可以连续.(访问)
c.getAccount().deposit(100); //存入100
c.getAccount().withdraw(1000); //取出1000
c.getAccount().withdraw(50);
}
}
【总结】:
1.关联关系:在属性中出现自定义类型的变量(引用数据类型)
2.对象数组(定义多个变量,本身就输入引用类型,也可以作为属性出现)
3.连续操作
c.getAccount().deposit(100);
c.getAccount().withdraw(1000);
c.getAccount().withdraw(50);
注意的是,前面的方法一定是有返回值,形成新的对象,再调用下一个属性或方法
关键字:package的使用
1.为了更好的实现项目中类的管理,提供包的概念
2.使用package声明类或接口所属的包,声明在源文件的首行
3.包,属于标识符,遵循标识符的命名规则、规范、见名知意
4.每"."一次就代表一层文件目录
补充:同一个包下,不能命名同名的接口、类
不同的包下,可以命名同名的接口、类
关键字:import的使用
import:导入
1.在源文件中显式的使用import结构导入指定包下的类、接口
2.声明在包的声明和类的声明之间
3.如果需要导入多个结构,则并列写出即可
4.可以用“xxx.*”的方式,表示可以导入xxx包下的所有结构
//并列写出
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
//或者:
import java.util.*;
5.如果使用的类或接口是java.lang包下定义的,则可以省略import结构
6.如果使用的类或接口是本包下定义的,则可以省略import结构
7.如果在源文件中,使用了不同包下的同名的类,必须至少一个类使用全类名的方式显式
8.使用“xxx.*”的方式表面可以调用xxx包下的所有结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入
9.import static:导入指定类或接口中静态结构(属性或方法)