[基础]]Java基础之面向对象(封装、多态、继承)
1、类与对象
(1)什么是类?
现实生活中是由很多很多对象组成的,基于对象抽出了类
生物学分类:界门纲目科属种 举例:六界中 --> 生物学分类:界门纲目科属种 -- 类
人:动物界-脊索动物门-哺乳纲-灵长目-人科-人属-智人种 玫瑰:植物界-被子植物门-双子叶植物纲-蔷薇目-蔷薇科-蔷薇属-玫瑰种
类:类型/类别,代表一类个体(种类) 类是抽象的,用来描述一类具有共同属性和行为事物的统称 类的组成:属性(具体值)、行为(可使用)
(2)什么是对象?
软件中真实存在的单个个体/东西 对象是根据类来创建的,类中有什么属性和行为,对象就有什么属性和行为。
(3)类是对象的模子(模板),对象是类的具体的实例(个体)
类 对象
月饼模子 月饼
学生 student 张三、李四
女朋友 girlFriend 小红,小芳
汽车 car 奔驰,宝马
(4)类中包含:
• 对象所共有的属性/特征--------------成员变量
• 对象所共有的行为/动作--------------方法
(5)如何创建类?如何创建对象?如何访问成员?
类是我们自己创造的一个数据类型(引用类型),创建出来的变量叫引用类型变量,简称引用。
格式:数据类型 引用类型变量 指向 new 对象 Airplane a1 = new Airplane();
//举例:
class girlFriend{
//成员变量/属性
String name;
int age;
String address;
//方法/行为
void eat(){}
void sleep(){}
}
class car{
//成员变量/属性
String type;
String color;
double price;
//方法/行为
void run(){}
void stop(){}
}
(6)一个类可以创建多个对象
同一类型所创建的对象,结构相同,数据不同。即:具体创建的对象不同。
对象是先声明,声明后会在给对象在内存里开辟空间,之后初始化,再进行使用。
//举例:
girlFriend xh = new girlFriend();
girlFriend xf = new girlFriend();
xh.name = "小红";
xh.age = 18;
xh.address = "心里";
xh.salary = 10000;//编译错误,没有这个属性
xh.eat();
xf.name="小芳";
...
飞机大战涉及到的相关问题:
(1)为什么在main的外面创建引用?
答:因为若将引用设计在main中,则引用就只能在main中被使用,其它方法都无法使用了,而World类中的会包含很多方法,这些方法中都会用到那一堆引用,所以将引用设计在main的外面,来扩大作用范围。
(2)为什么要单独创建action()方法来测试?
答:因为main()方法是static的,在static的方法中是无法访问那一堆引用的,所以单独创建一个非static的action()的来测试
(3)为什么在main中得先创建World对象,而后再调用action()方法?
答:因为main()方法是static的,在static的方法中是无法直接调用action()方法的,所以得先创建World 对象,而后再调用action()方法。
数据类型默认值:
byte,short,int,long,char-----------------0
float,double-----------------------------0.0
boolean----------------------------------false
引用类型---------------------------------null
2、方法:函数、过程
-
封装一段特定的业务逻辑(功能)
-
尽可能独立,一个方法只干一件事
-
方法可以被重复多次的调用
减少代码的重复,有利于代码的维护,有利于团队的协作。
(1)方法的定义
修饰词 返回值类型 方法名称(参数列表)
public static void methodName(String str)
{
方法体(具体代码)
}
(2)方法可以有返回值也可以没有返回值
-
无返回值,将返回值类型: void
-
有返回值,将返回值类型: 特定的数据类型(int,double)
-
注:何时有返回值?何时没有返回值?
a.若还需要用到方法中的某个数据 --- 有返回值
b. 若不需要用到方法中的某个数据 --- 无返回值
(3)方法的调用
-
无返回值:方法名(有参传参);
-
有返回值:数据类型 变量 = 方法名(有参传参);//要接收返回值
(4)return
• return ; 结束方法的执行(还可以在条件语句中跳出,结束方法)
• return 值; 结束方法的执行,返回结果给调用方
(5)方法的签名:方法名+参数列表
注意:java规定,在同一类中,不能出现签名相同的方法
(6)方法的重载(overload):-------------便于用户的访问
• 发生在同一类中,方法名相同,参数列表不同,方法体不同,与返回值类型无关 。
• 编译器在编译时会根据方法的签名自动绑定调用的方法 。
(7)方法的重写(override):重新写、覆盖
-
-
重写方法被调用时,看对象的类型(就近原则:先看派生类,再看超类)
//实例:
public class Demo05 {
public static void main(String[] args) {
Noo noo = new Noo();
noo.demo();
}
}
/*
* 1.我还想做中餐---不需要重写
* 2.我想改为西餐---需要重写
* 3.我想改成中西合璧---需要重写(先super中餐再加西餐)
*/
class Moo{
public void demo(){
System.out.println("中餐");
}
}
class Noo extends Moo{
// public void demo(){//重写demo方法(改做西餐)
// System.out.println("西餐");
// }
public void demo(){//重写demo方法(先super中餐再加西餐---中西合璧)
super.demo();//调用父类的中餐
System.out.println("西餐");//重写写的内容:西餐
}
}
-
遵循 " 两同两小一大 " 原则:
-
两同:
-
方法名相同
-
参数列表相同
-
-
两小:
-
派生类方法的返回值类型小于或等于超类方法的(一般用等于)
-
void时,必须相同
-
基本类型时,必须相同
-
引用类型时,必须小于或等于
-
-
派生类方法抛出的异常小于或等于超类方法的(一般用等于)
-
-
一大
-
派生类方法的访问权限大于或等于超类方法的(一般用等于)
-
-
//实例:
class Xoo{
void show(){}
double test(){return 3.14;}
Yoo say(){return null;}
Xoo sayHi(){return null;}
}
class Yoo extends Xoo{
void show(){}//void时,必须相同
//int show(){return 1;}
double test(){return 4.13;}//基本数据类型时,必须相同
//int test(){return 110;}
Yoo say(){return null;}//引用类型时,必须小于或等于:等于
//Xoo say(){return null;}//引用类型时,必须小于或等于:不能大于
Xoo sayHi(){return null;}//引用类型时,必须小于或等于:可等于
Yoo sayHi(){return null;}//引用类型时,必须小于或等于:可小于
//(注意上面两种重复定义了,实际使用用一行即可)
}
总结:重写与重载的区别
-
重写(Override/Overriding):(重新写,覆盖)
-
发生在父子类中,方法名相同,参数列表相同,方法体不同
-
“运行期绑定”,看对象的类型绑定方法
-
-
重载(Overload/Overloading):
-
发生在同一类中,方法名相同,参数列表不同,方法体不同
-
“编译期绑定”,看参数/引用类型的绑定方法
-
3、构造方法:构造函数、构造器、构建器
作用:复用给成员变量赋初始值代码
-
每个类都有的,java已经提供好的
-
方法名与类同名,没有返回值类型(连void都没有)
举例:方法名(类名一致)(有参传参){方法体}
-
给成员变量赋初始值
-
在创建(new)对象时被自动调用
-
若自己不写构造方法,则编译器默认提供一个无参的构造方法;
若自己写了构造方法,则编译器不再默认提供
-
构造方法可以重载(方法名相同,参数列表不同---方法签名不同)
4、this:指代当前对象
指代当前对象,哪个对象调用方法它指的就是哪个对象
只能用在方法中,方法中访问成员变量之前默认都有this.
this的用法:
-
this.成员变量名-----------访问成员变量
zs.study();-------------study()中的this指的就是zs
ls.study();-------------study()中的this指的就是ls
ww.study();-------------study()中的this指的就是ww
注意:当成员变量与局部变量同名时,若想访问成员变量则this不能省略
-
this.方法名()-------------调用方法(一般不用)
-
this()--------------------调用构造方法(应用率低)
5、成员变量与局部变量之间的关系
java规定:
-
成员变量和局部变量是可以同名的
-
用的时候默认采取的是就近原则
-
若想访问成员变量,则this不能省略
结论:当成员变量与局部变量同名时,若想访问成员变量,则this不能省略。
-
//举例:
class Student {
String name; //成员变量(整个类)
int age;
String address;
Student(){
this("无名氏",0,"未知"); //调用构造方法
}
Student(String name,int age,String address){ //局部变量(仅在当前方法中)
this.name = name;
this.age = age;
this.address = address;
}
void study(){
System.out.println(name+"在学习...");
}
void sayHi(){
System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);
}
}
6、空 null
表示空,没有指向任何对象
若引用的值为null,则该引用不能再进行任何点操作了,若操作,则发生NullPointerException空指针异常。
注意:创建对象时,引用类型的成员变量默认值为null。
引用类型之间画等号:(借房子,有两把钥匙)
(1)指向同一个对象
(2)通过一个引用对象的修改 会影响另一个引用的访问
基本类型之间画等号:(打印 复制)
(1)赋值
(2)对一个变量的修改,不会影响到另一个变量
7、数组
-
相同 数据类型 元素的集合
-
是一种数据类型(引用类型)
(1)数组的定义
声明整形数组arr,包含10个元素(每个元素int类型,默认值 0)
int[] arr = new int[10];
double[] b = new double[9];//默认值:0.0
boolean[] f = new boolean[5];//默认值:false
Ball[] ball = new Ball[100];//默认值:null
//举例:两个完全不同的数据类型
int a;
int[] arr;
//扩展:
/*
Java 中定义数组的语法有两种:
1.type arrayName[];
2.type[] arrayName;
其中,type为Java中的任意数据类型,包括基本类型和组合类型;
arrayName为数组名,必须是一个合法的标识符;
[]指明该变量是一个数组类型变量。
例如:int demoArray[];
*/
(2)数组的初始化
int[] arr = new int[4];//0,0,0,0 byte、short与int同理
int[] arr = {1,4,6,8};//1,4,6,8
int[] arr = new int[]{2,4,6,8};//2,4,6,8
int[] arr;
arr = {1,3,5,7};//编译错误,此方式仅限于声明同时初始化
arr = new int[]{1,3,5,7};//正确
(3)数组的访问:
//a.通过(数组名.length)可以获取数组的长度(元素的个数)
int[] arr = new int[10];
System.out.println(arr.length);
//b.通过下标/索引来访问数组中的元素。
int[] arr = new int[3];
arr[0] = 100;//给第一个元素赋值为100
arr[1] = 200;//给第二个元素赋值为200
arr[2] = 300;//给第三个元素赋值为300
arr[3] = 400;//(编译不错误)数组下标越界异常
System.out.println(arr[arr.length-1]);//输出最后一个元素的值
(4)数组的遍历
int[] arr = new int[10]; //0
for(int i=0;i<arr.length;i++){
arr[i] = (int)Math.random()*100;
}
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
for(int i=arr.length-1;i>=0;i--){
System.out.println(arr[i]);
}
(5)引用类型数组
//a.直接声明引用数组类型长度,再逐个元素依次初始化赋值
Student[] stus = new Student[3]; //创建Student数组对象
stus[0] = new Student("zs",25,"LF"); //创建Student对象
stus[1] = new Student("ls",26,"JMS");
stus[2] = new Student("ww",27,"SD");
System.out.println(stus[0].name); //输出第1个学生的名字
stus[1].age = 24; //修改第2个学生的年龄为24
stus[2].sayHi(); //第3个学生跟大家问好
for(int i=0;i<stus.length;i++){ //遍历所有学生
System.out.println(stus[i].name); //输出每个学生的名字
stus[i].sayHi(); //每个学生跟大家问好
}
//b.不指定数组长度,直接进行初始化赋值
Student[] stus = new Student[]{
new Student("zs",25,"LF"),
new Student("ls",26,"JMS"),
new Student("ww",27,"SD")
};
(6)数组的复制
//逐个复制
int[] a = {1,2,3,4,5};
int[] a1 = new int[9];
a1[0] = a[0];
a1[1] = a[1];
a1[2] = a[2];
a1[3] = a[3];
a1[4] = a[4];
for(int i=0;i<a.length;i++){ //元素位置不一致,操作麻烦
a1[i] = a[i];
}
/*
数组的复制:
a.System.arraycopy(a,1,a1,0,4); //灵活性好
a: 源数组
1: 源数组的起始下标
a1: 目标数组
0: 目标数组的起始下标
4: 要复制的元素个数
b. Arrays.copyOf(a,6);//灵活性差,需要导包,产生新数组,一般用来对数据进行缩容、扩容处理
a: 源数组
6:目标数组的长度(不会报错,多则补默认值,少则截取)
特殊用法:(扩容/缩容)
这种方式是重新创建了一个数组
Arrays.copyOf(a,a.length+1);
+: 扩容 -:缩容
*/
(7)数组的排序:冒泡排序、插入排序、选择排序
Arrays.sort(arr); //升序、效率高
冒泡原理:
• 四个数冒三轮
• 每一轮都是从第1个元素开始冒,每一次都是和它下一个元素比
• 冒出来的就不带它玩了
冒泡排序
package arraysort;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9,0,2,4,6,8};
System.out.println(Arrays.toString(bubbleSort(arr)));
}
//冒泡排序
public static int[] bubbleSort(int[] arr){
//每两个数比较一次,依次从第一个(0)到倒数第二个(arr.length-2)开始比较
for (int i = 0; i < arr.length-1; i++) {
//进行一趟冒泡排序,每次比较的次数减1
for (int j = 0; j < arr.length-1-i; j++) {
//如果前一个元素比后一个元素大,交换元素位置
//if(arr[j]>arr[j+1]){//升序
if(arr[j]<arr[j+1]){//降序
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
return arr;
}
}
插入排序
package arraysort;
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9,0,2,4,6,8};
System.out.println("交换之前:"+ Arrays.toString(arr));
int[] newArr = insertionSort(arr);
System.out.println("交换之后:"+Arrays.toString(newArr));
}
//插入排序:从第二个元素开始,每趟遍历前i个元素,将最小值插入到前面已排序好的序列
public static int[] insertionSort(int[] arr){
//第i趟排序,从第二个元素开始
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];//将每趟开始的元素作为待插入元素
int j;//最终要插入元素的下标
//每一趟排序,从第i个元素向前遍历,并且前面的元素要比插入元素大
for (j = i; j > 0 && arr[j-1]>temp; j--) {//升序
//for (j = i; j > 0 && arr[j-1]<temp; j--) {//降序
arr[j] = arr[j-1];//把大于需要插入的元素往后移
// 最后不大于temp的元素就空出来arr[j](for循环243243……,不满足2时退出)
}
arr[j] = temp;//将需要插入的数放到要插入的位置
}
return arr;
}
}
选择排序
package arraysort;
import java.util.Arrays;
public class SelectionSort {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9,0,2,4,6,8};
System.out.println("交换之前:"+Arrays.toString(arr));
int[] newArr = selectionSort(arr);
System.out.println("交换之后:"+Arrays.toString(newArr));
}
//选择排序:每趟选出最小的元素放在该趟的第一个位置,每趟循环的元素个数减1
public static int[] selectionSort(int[] arr){
//做第i趟排序(共进行:数组长度-1 次)
for (int i = 0; i < arr.length; i++) {
int index = i;//默认最小元素的下标为每趟开始的第1个元素
//求出该趟排序中最小元素的下标,每趟从起始下标的后一个元素开始
for (int j = index+1; j < arr.length; j++) {
if(arr[j]<arr[index]){//如果该元素比默认元素小(升序)
//if(arr[j]>arr[index]){//如果该元素比默认元素大(降序,求最大下标)
index = j;//用此元素下标替换默认最小元素下标
}
}
//在内层循环结束后,找到了本轮循环的最小元素及所对应的下标
//接下来进行交换,将最小元素放到该轮排序的第一个元素位置
if(i!=index){//如果该轮的第一个元素就是最小元素,就不用交换了
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
}
return arr;
}
}
8、继承 extends
-
作用:代码复用
-
通过extends(扩展)来实现继承
(1)超类/父类:派生类所共有的属性和行为**
派生类/子类:派生类所特有的属性和行为**
(2)先有的派生类,再将派生类中的共有属性和行为泛化出超类。
子类继承父类的一切,除了私有方法和构造方法。
派生类可以访问: 超类的+派生类的,超类只能访问超类的
(3)一个超类可以有多个派生类, 一个派生类只能有一个超类------单一继承
(4)继承具有传递性
举例:
程序中的继承:代码不用自己写,自己也能用。
生活中的继承:钱不用自己挣,自己也能花。
继承皇位:江山不用自己打,自己也能坐。
//举例:
class Aoo{//可以访问a
int a;
}
class Boo extends Aoo{//可以访问b+a
int b;
}
class Coo extends Boo{//可以访问c+b+a
int c;
}
(5)java规定:构造派生类之前必须先构造超类
-
在派生类的构造方法中,若自己没有调用超类的构造方法,则默认super()调用超类的无参构造方法
-
在派生类的构造方法中,若自己调用了超类的构造方法,则不再默认提供
-
说明:super()调用超类构造方法必须位于派生类构造方法的第一行
//举例:
class Student extends Person{
String stuId;
/*public Student(){//无参构造
super();//无参构造
}*/
public Student(){//有参构造
super("zs");//有参构造,必须传参
}
public Student(String name){
super(name);//间接传参
}
void study(){}
}
9、super:指代当前对象的超类对象
super的用法:(区别调用超类还是派生的变量/方法)
-
super.成员变量名-----------访问超类的成员变量
-
super.方法名()---------------调用超类的方法(重名时,必须加super.;不重名时,可不写super,前提是子类已经继承父类)
-
super()-------------------------调用超类的无参构造方法
//实例1:
public class SuperDemo {
public static void main(String[] args) {
Boo o = new Boo();
}
}
//超类无参数
class Aoo{
Aoo(){
System.out.println("超类构造方法");
}
}
class Boo extends Aoo{
Boo(){
//super(); //默认的--调用超类的无参构造
System.out.println("派生类构造方法");
}
}
//超类有参数
class Coo{
Coo(int a){
}
}
class Doo extends Coo{
Doo(){
super(5); //调用超类的有参构造
}
/*
//如下代码为默认的:
Doo(){
super();
}
*///使用默认无参的超类构造会报错
}
//实例2:
class Person{
String name;
int age;
String address;
public Person(){}//无参
public Person(String name){//有参构造
this.name = name;
}
void eat(){}
void sleep(){}
}
class Student extends Person{
String stuId;
/*public Student(){//无参构造
super();//无参构造
}*/
public Student(){//有参构造
super("zs");//有参构造,必须传参
}
public Student(String name){
super(name);//间接传参
}
void study(){
super.address="123";
super.sleep();//可写可不写,前提是不重名
eat();
}
}
//实例3:
public FlyingObject(double x, double y, double width, double height) {
super();//默认每个类都有一个父类Object(顶级超类),调用此父类无参构造方法,可不写(删除此行),默认调用无参构造方法。
this.x = x;
this.y = y;
this.