面向对象(上)
1.面向对象编程
1.1面向对象和面向过程的区别
面向过程:面向过程(Procedure Oriented)是一种以过程为中心的编程思想,它关注的是将问题分解成一个个详细的步骤,并通过函数实现每一个步骤,然后依次调用这些函数来解决问题。常见的面向过程的语言有C语言。
面向对象: 面向对象编程(Object-Oriented Programming,简称OOP)是一种计算机编程模型,它围绕数据或对象来组织软件设计,而不是围绕功能和逻辑。在面向对象的编程中,更强调对象与对象之间的交互,对象的属性和方法都被封装在对象内部。面向对象编程具有封装性、继承性和多态性三大特性。常见的面向对象的语言有Java,Python,PHP等。
举例:到超市购物,超市有500种商品,买了5样商品
面向过程思考:到超市购物,买了5样商品,回家放入冰箱
面向对象思考:到超市购物,超市所有商品创建实体,买了5样商品,回家放入冰箱
需求变更:
面向过程:又买了猪肉,修改代码,才能放入冰箱
面向对象:猪肉实体已经买了,无需修改代码,就把猪肉放入冰箱即可
1.2 类和对象
1.2.1 类
类是一类事物具有相同特性的抽象描述,是一组是一组相关属性和行为的集合。
1.2.2 对象
对象是一类事物的一个具体个体(对象并不是找个女朋友)。即对象是类的一个实例,必然具备该类事物的属性和行为。
1.2.3 类与对象的关系
-
类是对一类事物的描述,是抽象的。是相同属性和方法的一组对象的集合。
-
对象是一类事物的实例,是具体的。
-
类是对象的模板,对象是类的实体。是能够看的到,摸得到的具体实体。
-
世间万物皆对象
1.3 定义类
【修饰符】 class 类名{
}
public class Student{
}
创建对象:
类名 对象名 = new 类名();
2. 包
2.1 包的作用
(1)可以避免类重名:有了包之后,类的全名称就变为:包.类名
(2)可以控制某些类型或成员的可见范围
如果某个类型或者成员的权限修饰缺省的话,那么就仅限于本包使用。
(3)分类组织管理众多的类
2.2 声明包
关键字:package
package 包名;
注意:
(1)必须在源文件的代码首行
(2)一个源文件只能有一个声明包的package语句
包的命名规范和习惯: (1)所有单词都小写,每一个单词之间使用.分割 (2)习惯用公司的域名倒置开头
例如:com.haogu.xxx;
建议大家取包名时不要使用“java.xx"包
2.3 如何跨包使用类
注意:只有public的类才能被跨包使用
(1)使用类型的全名称
例如:java.util.Scanner input = new java.util.Scanner(System.in);
(2)使用import 语句之后,代码中使用简名称
import语句告诉编译器到哪里去寻找类。
3 成员变量
3.1 如何声明成员变量(类变量)
【修饰符】 class 类名{
【修饰符】 数据类型 成员变量名;
}位置要求:必须在类中,方法外
public class Person{
String name;
char gender;
int age;
}
3.2 对象的实例变量
实例变量的特点
(1)实例变量的值是属于某个对象的
-
必须通过对象才能访问实例变量
-
每个对象的实例变量的值是独立的
(2)实例变量有默认值
代码示例:
public class Car {
// 成员变量 实例变量
String carName;
String carBrand;
String carColor;
double price;
float jiaSu;
public void status(){
System.out.println("您的宝驹正在玩命生产中!");
}
public void showInfo(){
System.out.println("车名:"+carName+",品牌:" + carBrand + ",颜色:" + carColor + ",价格:" + price + "w"+ ",百米加速" + jiaSu + "s");
}
public static void fly(){
System.out.println("飞车");
}
}
public class TestCar01 {
public static void main(String[] args) {
Car car1 = new Car();
car1.carName = "xiaomiSU7";
car1.carBrand = "xiaomi";
car1.carColor = "霞光紫";
car1.price = 21.89;
car1.jiaSu = 2.3f;
car1.status();;
car1.showInfo();
}
}
4.方法
4.1 概念
方法也叫函数,是一组代码语句的封装,从而实现代码重用,从而减少冗余代码,通常它是一个独立功能的定义,方法是一个类中最基本的功能单元。
4.2 方法的特点
(1)必须先声明后使用
类,变量,方法等都要先声明后使用
(2)不调用不执行,调用一次执行一次。
4.3 如何声明方法
1、声明方法的位置
声明方法的位置 必须在类中 方法外 ,即不能再一个方法中直接定义另一个方法
声明方法的位置==必须在类中方法外==,即不能在一个方法中直接定义另一个方法。
声明位置示例:
类{ 方法1(){ } 方法2(){ } }
错误示例:
类{ 方法1(){ 方法2(){ //位置错误 } } }
2.声明方法的格式
【修饰符】 返回值类型 方法名(【形参列表 】)【throws 异常列表】{
方法体的功能代码
}
方法名:给方法起名要见名知意。
4.4 如何调用实例方法
对象.非静态方法(【实参列表】)
4.5 实例方法使用当前对象的成员
在实例方法中还可以使用当前对象的其他成员。在Java中当前对象用this表示。
-
this:在实例方法中,表示调用该方法的对象
-
若无歧义,完全可以省略this。
4.6 实例变量与局部变量的区别
1、声明位置和方式
(1)实例变量:在类中方法外
(2)局部变量:在方法体{}中或方法的形参列表、代码块中
2、在内存中存储的位置不同
(1)实例变量:堆
(2)局部变量:栈
3、生命周期
(1)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被GC回收而消亡, 而且每一个对象的实例变量是独立的。
(2)局部变量:和方法调用的生命周期一样,每一次方法被调用而在存在,随着方法执行的结束而消亡, 而且每一次方法调用都是独立。
4、作用域
(1)实例变量:通过对象就可以使用,本类中“this.,没有歧义还可以省略this.”,其他类中“对象.” (2)局部变量:出了作用域就不能使用
5、修饰符(后面来讲)
(1)实例变量:public,protected,private,final,volatile,transient等
(2)局部变量:final
6、默认值
(1)实例变量:有默认值
(2)局部变量:没有,必须手动初始化。其中的形参比较特殊,靠实参给它初始化
5.参数问题
5.1特殊参数之:可变参数
【修饰符】 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型... 形参名){ }
可变参数的特点和要求:
(1)一个方法最多只能有一个可变参数
(2)如果一个方法包含可变参数,那么可变参数必须是形参列表的最后一个
(3)在声明它的方法中,可变参数当成数组使用
(4)其实这个书写...
【修饰符】 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型[] 形参名){ }
后面的定义方式,在调用时必须传递数组,而前者更灵活,既可以传递数组又可以直接传递数组的元素,这样会更加灵活
public class SumToolss {
// 根据数组求和
public int sum1(int[] arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
// 可变参数当做数组来进行使用了
public int sum2(int... numArr) {
int sum = 0;
for (int i = 0; i < numArr.length; i++) {
sum += numArr[i];
}
return sum;
}
public int sum3(int multiple, int... numArr) {
int sum = 0;
for (int i = 0; i < numArr.length; i++) {
sum += numArr[i];
}
return sum * multiple;
}
}
5.2 特殊参数之二:命令行参数(了解)
通过命令行给main方法的形参传递的实参称为命令行参数
public class TestCommandParam{
//形参:String[] args
public static void main(String[] args){
System.out.println(args);
System.out.println(args.length);
for(int i=0; i<args.length; i++){
System.out.println("第" + (i+1) + "个参数的值是:" + args[i]);
}
}
}
6 方法的重载
-
方法重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
-
参数列表:数据类型个数不同,数据类型不同(按理来说数据类型顺序不同也可以,但是很少见,也不推荐,逻辑上容易有歧义)。
-
重载方法调用:JVM通过方法的参数列表,调用匹配的方法。
-
先找个数、类型最匹配的
-
再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错
-
案例,用重载实现:
(1)定义方法求两个整数的最大值
(2)定义方法求三个整数的最大值
(3)定义方法求两个小数的最大值
7. 方法的递归调用
递归调用:方法自己调用自己的现象就称为递归。
递归的分类:
-
递归分为两种,直接递归和间接递归。
-
直接递归称为方法自身调用自己。
-
间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。
注意事项:
- 递归一定要有条件限制,保证递归能停止下来,否则易发生栈内存溢出
- 递归深度不能过深,否则效率低下,或也容易发生栈内存溢出
经典案例 --斐波那契额数列(Fibonacci )
/**
* 递归:自己调用自己
* 递归一定要有条件限定,保证递归能够停止下来
* 求第几个斐波那契数列的值
*/
public class Feibo {
int fei(int n){
// 小于1
if(n<1){
return 1;
}
// 等于1或者等于2
if(n==1||n==2){
return 1;
}
return fei(n-2)+fei(n-1);
/**
* n=8
* 第一次 : fei(6)+fei(7)
* 第二次 :fei(4) +fei(5)+fei(5)+fei(6)
* 第三次 :fei(4) +fei(5)+fei(5)+fei(6)
* fei(3)+ 1+ fei(4)+fei(3)......
* 1+1+1.....
*/
}
public static void main(String[] args) {
// 计算斐波那契数列(Fibonacci)的第n个值,斐波那契数列满足如下规律
// 1,1,2,3,5,8,13,21,34 ....
// 即从第三个数开始,一个数等于前两个数之和。假设f(n)代表斐波那契数列的第n个值,那么f(n)满足:
//
// f(n) = f(n-2) + f(n-1);
// 实例化的目的调用fei
Feibo feibo = new Feibo();
int rel1 = feibo.fei(0);
int rel2 = feibo.fei(8);
int rel3 = feibo.fei(10);
System.out.println(rel1);
System.out.println(rel2);
System.out.println(rel3);
}
}
8.对象数组
数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组来装。
即数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用数据类型是,我们称为对象数组。
注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException。
案例:
测试主方法类
import com.haogu.day0412.model.Dog;
// 数组对象中得先设置值,然后再使用对象中的属性和方法
// 排序时,是比较对象的属性值
// 临时量 进行初始化新的对象
public class TestDog2 {
public static void main(String[] args) {
Dog[] dog = new Dog[5];
// 第一个
Dog dog1 = new Dog();
dog1.name = "小黑";
dog1.age = 2;
dog[0] = dog1;
// 第二个
Dog dog2 = new Dog();
dog2.name = "老黄";
dog2.age = 10;
dog[1] = dog2;
// 第三个
Dog dog3 = new Dog();
dog3.name = "小白";
dog3.age = 4;
dog[2] = dog3;
// 第四个
Dog dog4 = new Dog();
dog4.name = "薯条";
dog4.age = 20;
dog[3] = dog4;
// 第五个
Dog dog5 = new Dog();
dog5.name = "可乐";
dog5.age = 7;
dog[4] = dog5;
/**
* 根据狗的年龄进行狗的对象数据的排序
* 由小到大
*/
for (int i = 0; i < dog.length - 1; i++) {
for (int j = 0; j < dog.length - i - 1; j++) {
if (dog[j].age > dog[j + 1].age) {
Dog temp = new Dog();
temp = dog[j];
dog[j] = dog[j + 1];
dog[j + 1] = temp;
}
}
}
String dogMsg = "";
for (int i = 0; i < dog.length; i++) {
dogMsg += dog[i].name + dog[i].age + "岁 ";
}
System.out.println(dogMsg);
}
}
方法类
public class Dog {
// 狗的名字
public String name;
// 狗的年龄
public int age;
}
写在最后
上篇结束,持续更新中