java-面向对象
第八章 面向对象
面向过程和面向对象的区别
面向过程:当事件比较简单的时候,利用面向过程,注重的是事件的具体的步骤/过程,注重的是过程中的具体的行为,以函数为最小单位,考虑怎么做。
面向对象:注重找“参与者”,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
案例:
人把大象装进冰箱:
面向过程:
函数1:打开冰箱(){人站在冰箱前,打开冰箱,冰箱卡到30度角的时候,冰箱的灯打开了.........}
函数2:储存大象(){大象先迈左腿,再迈右退,考虑冰箱能不能装下......}
函数3:关闭冰箱(){人站在冰箱前,关闭冰箱,冰箱开到30度角的时候,冰箱的灯关闭了..........}
面向对象:
人{
打开(冰箱){
冰箱.打开();
}
存储(大象){
大象.进入();
}
关闭(冰箱){
冰箱.关闭();
}
}
冰箱{
打开(){ 1.2.3.}
关闭(){}
}
柜子{
}
大象{
进入(冰箱){
}
}
面向过程 ---> 面向对象 , 其实就是由执行者 ---> 指挥者的 一个过渡
面向过程:编年体
面向对象:纪传体
二者相辅相成,并不是对立的。解决复杂问题,通过面向对象方式便于我们从宏观上把握事物之间复杂的关系、方便我们分析整个系统;具体到微观操作,仍然使用面向过程方式来处理
类和对象的关系
【1】万事万物皆对象
【2】
对象:具体的事物,具体的实体,具体的实例,模板下具体的产品
类:对对象向上抽取出像的部分,公共的部分,形成类,类是抽象的,是一个模板
【3】一般在写代码的时候先写类,然后在根据类创建对应的对象。
面向对象三个阶段
面向对象三个阶段:
【1】面向对象分析OOA -- Object Oriented Analysis
对象:张三,王五,朱六,你,我
抽取出一个类----》人类
类里面有什么:
动词--》动态特性--》方法
名词--》静态特性--》属性
【2】面向对象设计OOD -- Object Oriented Design
先有类,再有对象:
类:人类: Person
对象:zhangsan ,lisi,zhuliu
【3】面向对象编程OOP -- Object Oriented Programming
创建类:
(1)属性(field 成员变量)
属性用于定义该类或该类对象包含的数据或者说静态特征。属性作用范围是整个类体。
属性定义格式:
[修饰符] 方法返回值类型 方法名(形参列表) { // n条语句 } |
(2)方法
方法用于定义该类或该类实例的行为特征和功能实现。方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
方法定义格式:
[修饰符] 方法返回值类型 方法名(形参列表) { // n条语句 } |
void代表没有返回值;方法的作用:重用代码,封装功能,便于修改
代码:
1 package com.llh; 2 /** 3 * @Auther: llh 4 * 创建类:人类 5 */ 6 public class Person { 7 //名词---》属性---》成员变量---》放在类中方法外(注意:我们只把有需要的内容写到代码里,不相关的东西不要放在代码中) 8 int age ;//年龄 9 String name;//姓名 10 double height;//身高 11 double weight;//体重 12 //动词---》方法 13 //吃饭 14 public void eat(){ 15 int num = 10;//局部变量:放在方法中 16 System.out.println("我喜欢吃饭"); 17 } 18 //睡觉: 19 public void sleep(String address){ 20 System.out.println("我在"+address+"睡觉"); 21 } 22 //自我介绍: 23 public String introduce(){ 24 return "我的名字是:"+name+",我的年龄是:"+age+",我的身高是:"+height+",我的体重是:"+weight; 25 } 26 }
创建对象
1 package com.llh; 2 /** 3 * @Auther: llh 4 */ 5 public class Test {//测试类 6 //这是一个main方法,是程序的入口: 7 public static void main(String[] args) { 8 //创建一个人类的具体的对象/实例: 9 //创建一个对象,对象的名字叫:zs 10 //Person 属于 引用数据类型 11 //第一次加载类的时候,会进行类的加载,初始化创建对象的时候,对象的属性没有给赋值,有默认的初始化的值。 12 Person zs = new Person(); 13 zs.name = "张三"; 14 zs.age = 19; 15 zs.height = 180.4; 16 zs.weight = 170.4; 17 //再创建一个对象: 18 //再次创建类的时候,就不会进行类的加载了,类的加载只在第一次需要的时候加载一次 19 Person ls = new Person(); 20 ls.name = "李四"; 21 ls.age = 18; 22 ls.height = 170.6; 23 ls.weight = 160.5; 24 //对属性值进行读取: 25 System.out.println(zs.name); 26 System.out.println(ls.age); 27 //对方法进行操作: 28 //不同的对象,属性有自己的特有的值,但是方法都是调用类中通用的方法。 29 //属性:各个对象的属性是独立的, 30 //方法:各个对象的方法是共享的。 31 zs.eat(); 32 ls.eat(); 33 zs.sleep("教室"); 34 /*String str = zs.introduce(); 35 System.out.println(str);*/ 36 System.out.println(zs.introduce()); 37 } 38 }
局部变量和成员变量的区别
区别1:代码中位置不同
成员变量:类中方法外定义的变量
局部变量:方法中定义的变量 代码块中定义的变量
区别2:代码的作用范围
成员变量:当前类的很多方法
局部变量:当前一个方法(当前代码块)
区别3:是否有默认值
成员变量:有
局部变量:没有
引用数据类型: null
区别4:是否要初始化
成员变量:不需要,不建议初始化,后续使用的时候再赋值即可
局部变量:一定需要,不然直接使用的时候报错
区别5:内存中位置不同
成员变量:堆内存
局部变量:栈内存
区别6:作用时间不同
成员变量:当前对象从创建到销毁
局部变量:当前方法从开始执行到执行完毕
代码:
1 public class Student { 2 byte e; 3 short s; 4 int c ;//成员变量:在类中方法外 5 long num2; 6 float f ; 7 double d; 8 char ch; 9 boolean bo; 10 String name; 11 public void study(){ 12 int num = 10 ; //局部变量:在方法中 13 System.out.println(num);//10 14 //int num ;重复命名,出错了 15 { 16 int a;//局部变量:在代码块中 17 } 18 int a; 19 if(1==3){ 20 int b; 21 } 22 System.out.println(c); 23 } 24 public void eat(){ 25 System.out.println(c); 26 } 27 //这是一个main方法,是程序的入口: 28 public static void main(String[] args) { 29 Student s = new Student(); 30 System.out.println(s.c); 31 System.out.println(s.bo); 32 System.out.println(s.ch); 33 System.out.println(s.d); 34 System.out.println(s.e); 35 System.out.println(s.f); 36 System.out.println(s.name); 37 System.out.println(s.num2); 38 System.out.println(s.s); 39 s.d = 10.4; 40 } 41 }
运行结果:
构造器
1 public class Person { 2 //构造器:没有任何参数的构造器我们叫做:空参构造器--》空构造器 3 public Person(){ 4 /*age = 19; 5 name = "lili"; 6 height = 169.5;*/ 7 } 8 //属性: 9 String name; 10 int age; 11 double height; 12 //方法: 13 public void eat(){ 14 System.out.println("我喜欢吃饭"); 15 } 16 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //创建一个Person类的具体的对象/实例/实体: 5 /* 6 创建对象的过程: 7 1.第一次遇到Person的时候,进行类的加载(只加载一次) 8 2.创建对象,为这个对象在堆中开辟空间 9 3.为对象进行属性的初始化动作 10 new关键字实际上是在调用一个方法,这个方法叫构造方法(构造器) 11 调用构造器的时候,如果你的类中没有写构造器,那么系统会默认给你分配一个构造器,只是我们看不到罢了。 12 可以自己显式 的将构造器编写出来: 13 构造器的格式: 14 [修饰符] 构造器的名字(){ 15 } 16 构造器和方法的区别: 17 1.没有方法的返回值类型 18 2.方法体内部不能有return语句 19 3.构造器的名字很特殊,必须跟类名一样 20 构造器的作用:不是为了创建对象,因为在调用构造器之前,这个对象就已经创建好了,并且属性有默认的初始化的值。 21 调用构造器的目的是给属性进行赋值操作的。 22 注意:我们一般不会在空构造器中进行初始化操作,因为那样的话每个对象的属性就一样了。 23 实际上,我们只要保证空构造器的存在就可以了,里面的东西不用写 24 */ 25 Person p = new Person(); 26 System.out.println(p.age); 27 System.out.println(p.name); 28 System.out.println(p.height); 29 Person p2 = new Person(); 30 System.out.println(p2.age); 31 System.out.println(p2.name); 32 System.out.println(p2.height); 33 } 34 }
构造器的重载
1 public class Person { 2 //属性: 3 String name; 4 int age; 5 double height; 6 //空构造器 7 public Person(){ 8 } 9 public Person(String name,int age,double height){ 10 //当形参名字和属性名字重名的时候,会出现就近原则: 11 //在要表示对象的属性前加上this.来修饰 ,因为this代表的就是你创建的那个对象 12 this.name = name; 13 this.age = age; 14 this.height = height; 15 } 16 public Person(String a,int b){ 17 name = a; 18 age = b; 19 } 20 //方法: 21 public void eat(){ 22 System.out.println("我喜欢吃饭"); 23 } 24 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 /* 5 1.一般保证空构造器的存在,空构造器中一般不会进行属性的赋值操作 6 2.一般我们会重载构造器,在重载的构造器中进行属性赋值操作 7 3.在重载构造器以后,假如空构造器忘写了,系统也不会给你分配默认的空构造器了,那么你要调用的话就会出错了。 8 4. 当形参名字和属性名字重名的时候,会出现就近原则: 9 在要表示对象的属性前加上this.来修饰 ,因为this代表的就是你创建的那个对象 10 */ 11 Person p = new Person(); 12 /*p.age = 19; 13 p.name = "lili"; 14 p.height = 180.4;*/ 15 Person p2 = new Person("lili",19,180.4); 16 System.out.println(p2.age); 17 System.out.println(p2.height); 18 System.out.println(p2.name); 19 } 20 }
内存分析
代码1
1 public class Person { 2 int id; 3 int age; 4 5 public static void main(String args[]){ 6 Person p1= new Person(); 7 } 8 }
内存分析:
代码2
1 public class Person { 2 int id; 3 int age; 4 String school; 5 public Person (int a,int b,String c){ 6 id=a; 7 age=b; 8 school=c; 9 } 10 public static void main(String args[]){ 11 Person p= new Person(1,20, "海淀"); 12 } 13 }
代码3
1 class Person{ 2 int id; 3 int age; 4 String school; 5 Person (int a,int b,String c){ 6 id=a; 7 age=b; 8 school=c; 9 } 10 11 public void setAge(int a){ 12 age=a; 13 } 14 }
1 public class Test { 2 public static void main(String[] args) { 3 Test t=new Test(); 4 int age=40; 5 Person tom=new Person(1,20,"海淀"); 6 Person jack=new Person(2,30,"朝阳"); 7 t.change1(age); 8 t.change2(tom); 9 t.change3(jack); 10 System.out.println(age); //40 11 System.out.println("id:"+jack.id+",age:"+jack.age+",school:"+jack.school); //id:2,age:66,school:"朝阳" 12 } 13 public void change1(int i){ 14 i=3366; 15 } 16 17 public void change2(Person p){ 18 p=new Person(3,22,"西城"); 19 } 20 21 public void change3(Person p){ 22 p.setAge(66); 23 } 24 25 }
this
【1】创建对象的过程:
(1)在第一次遇到一个类的时候,对这个类要进行加载,只加载一次。
(2)创建对象,在堆中开辟空间
(3)对对象进行初始化操作,属性赋值都是默认的初始值。
(4)new关键字调用构造器,执行构造方法,在构造器中对属性重新进行赋值
this:
从上面的效果能够看到:this指代的就是当前对象:
内存:
this关键字 用法:
(1)this可以修饰属性:
总结:当属性名字和形参发生重名的时候,或者 属性名字 和局部变量重名的时候,都会发生就近原则,所以如果我要是直接使用变量名字的话就指的是离的近的那个形参或者局部变量,这时候如果我想要表示属性的话,在前面要加上:this.修饰
如果不发生重名问题的话,实际上你要是访问属性也可以省略this.
1 public class Person { 2 //属性 3 int age; 4 String name; 5 double height; 6 //空构造器 7 public Person(){ 8 } 9 //有参构造器 10 public Person(int age,String name,double height){ 11 this.age = age; 12 this.name = name; 13 this.height = height; 14 } 15 //方法: 16 public void eat(){ 17 int age = 10; 18 System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age 19 System.out.println(this.age);//这里指代的就是属性的age 20 System.out.println("我喜欢吃饭"); 21 } 22 }
(2)this修饰方法:
总结:在同一个类中,方法可以互相调用,this.可以省略不写。
1 public class Person { 2 //属性 3 int age; 4 String name; 5 double height; 6 //空构造器 7 public Person(){ 8 } 9 //有参构造器 10 public Person(int age,String name,double height){ 11 this.age = age; 12 this.name = name; 13 this.height = height; 14 } 15 //方法: 16 /*public void eat(){ 17 int age = 10; 18 System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age 19 System.out.println(this.age);//这里指代的就是属性的age 20 System.out.println("我喜欢吃饭"); 21 }*/ 22 public void play(){ 23 /*this.*/eat(); 24 System.out.println("上网"); 25 System.out.println("洗澡"); 26 } 27 public void eat(){ 28 System.out.println(/*this.*/age); 29 System.out.println("吃饭"); 30 } 31 }
(3)this可以修饰构造器:
总结:同一个类中的构造器可以相互用this调用,注意:this修饰构造器必须放在第一行
1 public class Person { 2 //属性 3 int age; 4 String name; 5 double height; 6 //空构造器 7 public Person(){ 8 } 9 //有参构造器 10 public Person(int age,String name,double height){ 11 this(age,name); 12 this.height = height; 13 } 14 public Person(int age,String name){ 15 this(age); 16 this.name = name; 17 } 18 public Person(int age){ 19 this.age = age; 20 } 21 //方法: 22 /*public void eat(){ 23 int age = 10; 24 System.out.println(age);//就近原则,age指的是离它近的age--》局部变量的age 25 System.out.println(this.age);//这里指代的就是属性的age 26 System.out.println("我喜欢吃饭"); 27 }*/ 28 public void play(){ 29 /*this.*/eat(); 30 System.out.println("上网"); 31 System.out.println("洗澡"); 32 } 33 public void eat(){ 34 System.out.println(/*this.*/age); 35 System.out.println("吃饭"); 36 } 37 }
static
【1】static可以修饰:属性,方法,代码块,内部类。
【2】static修饰属性;
1 public class Test { 2 //属性: 3 int id; 4 static int sid; 5 //这是一个main方法,是程序的入口: 6 public static void main(String[] args) { 7 //创建一个Test类的具体的对象 8 Test t1 = new Test(); 9 t1.id = 10; 10 t1.sid = 10; 11 Test t2 = new Test(); 12 t2.id = 20; 13 t2.sid = 20; 14 Test t3 = new Test(); 15 t3.id = 30; 16 t3.sid = 30; 17 //读取属性的值: 18 System.out.println(t1.id); 19 System.out.println(t2.id); 20 System.out.println(t3.id); 21 System.out.println(t1.sid); 22 System.out.println(t2.sid); 23 System.out.println(t3.sid); 24 } 25 }
内存分析:
一般官方的推荐访问方式:可以通过类名.属性名的方式去访问:
static修饰属性总结:
(1)在类加载的时候一起加载入方法区中的静态域中
(2)先于对象存在
(3)访问方式: 对象名.属性名 类名.属性名(推荐)
static修饰属性的应用场景:某些特定的数据想要在内存中共享,只有一块 --》这个情况下,就可以用static修饰的属性
1 package com.llh; 2 3 public class GuiYangStudent { 4 //属性: 5 String name; 6 int age; 7 static String school; 8 //这是一个main方法,是程序的入口: 9 public static void main(String[] args) { 10 GuiYangStudent.school = "贵阳学院"; 11 //创建学生对象: 12 GuiYangStudent s1 = new GuiYangStudent(); 13 s1.name = "张三"; 14 s1.age = 19; 15 //s1.school = "贵阳学院"; 16 GuiYangStudent s2 = new GuiYangStudent(); 17 s2.name = "李四"; 18 s2.age = 21; 19 //s2.school = "贵阳学院"; 20 System.out.println(s2.school); 21 } 22 }
属性:
静态属性 (类变量)
非静态属性(实例变量)
【3】static修饰方法;
1 public class Demo { 2 int id; 3 static int sid; 4 public void a(){ 5 System.out.println(id); 6 System.out.println(sid); 7 System.out.println("------a"); 8 } 9 //1.static和public都是修饰符,并列的没有先后顺序,先写谁后写谁都行 10 static public void b(){ 11 //System.out.println(this.id);//4.在静态方法中不能使用this关键字 12 //a();//3.在静态方法中不能访问非静态的方法 13 //System.out.println(id);//2.在静态方法中不能访问非静态的属性 14 System.out.println(sid); 15 System.out.println("------b"); 16 } 17 //这是一个main方法,是程序的入口: 18 public static void main(String[] args) { 19 //5.非静态的方法可以用对象名.方法名去调用 20 Demo d = new Demo(); 21 d.a(); 22 //6.静态的方法可以用 对象名.方法名去调用 也可以 用 类名.方法名 (推荐) 23 Demo.b(); 24 d.b(); 25 26 } 27 }
代码块
【1】类的组成:属性,方法,构造器,代码块,内部类
【2】代码块分类:普通块,构造块,静态块,同步块(多线程)
【3】代码:
1 public class Test { 2 //属性 3 int a; 4 static int sa; 5 //方法 6 public void a(){ 7 System.out.println("-----a"); 8 { 9 //普通块限制了局部变量的作用范围 10 System.out.println("这是普通块"); 11 System.out.println("----000000"); 12 int num = 10; 13 System.out.println(num); 14 } 15 //System.out.println(num); 16 //if(){} 17 //while(){} 18 } 19 public static void b(){ 20 System.out.println("------b"); 21 } 22 //构造块 23 { 24 System.out.println("------这是构造块"); 25 } 26 //静态块 27 static{ 28 System.out.println("-----这是静态块"); 29 //在静态块中只能方法:静态属性,静态方法 30 System.out.println(sa); 31 b(); 32 } 33 //构造器 34 public Test(){ 35 System.out.println("这是空构造器"); 36 } 37 public Test(int a){ 38 this.a = a; 39 } 40 //这是一个main方法,是程序的入口: 41 public static void main(String[] args) { 42 Test t = new Test(); 43 t.a(); 44 Test t2 = new Test(); 45 t2.a(); 46 } 47 }
总结:
(1)代码块执行顺序:
最先执行静态块,只在类加载的时候执行一次,所以一般以后实战写项目:创建工厂,数据库的初始化信息都放入静态块。
一般用于执行一些全局性的初始化操作。
再执行构造块,(不常用)
再执行构造器,
再执行方法中的普通块。
包,import
【1】生活案例:
邮寄快递:中国.北京.通州区.****小区.5号楼.3单元.101房.赵珊珊
历史:常山赵子龙
【2】包的作用:
为了解决重名问题(实际上包对应的就是盘符上的目录)
解决权限问题
【3】创建包:
包名定义:
(1)名字全部小写
(2)中间用.隔开
(3)一般都是公司域名倒着写 : com.jd com.msb
(4)加上模块名字:
com.jd.login com.jd.register
(5)不能使用系统中的关键字:nul,con,com1---com9.....
(6)包声明的位置一般都在非注释性代码的第一行:
【4】导包问题:
1 package com.llh; 2 3 import com.llh1.Person; //导包:就是为了进行定位 4 import com.llh1.Demo; 5 6 import java.util.Date; 7 8 public class Test { 9 //这是一个main方法,是程序的入口: 10 public static void main(String[] args) { 11 new Person(); 12 new Date(); 13 new java.sql.Date(1000L);//在导包以后,还想用其他包下同名的类,就必须要手动自己写所在的包。 14 new Demo(); 15 } 16 }
总结:
(1)使用不同包下的类要需要导包: import **.*.*; 例如:import java.util.Date;
(2)在导包以后,还想用其他包下同名的类,就必须要手动自己写所在的包。
(3)同一个包下的类想使用不需要导包,可以直接使用。
(4)在java.lang包下的类,可以直接使用无需导包:
(5)IDEA中导包快捷键:alt+enter
可以自己设置自动导包
(6)可以直接导入*:
【5】在Java中的导包没有包含和被包含的关系:
设置目录平级的格式(不是包含和被包含的显示):
【6】静态导入:
1 package com.llh; 2 3 //静态导入: 4 import static java.lang.Math.*; 5 //导入:java.lang下的Math类中的所有静态的内容 6 7 public class Test { 8 //这是一个main方法,是程序的入口: 9 public static void main(String[] args) { 10 System.out.println(random()); 11 System.out.println(PI); 12 System.out.println(round(5.6)); 13 } 14 //在静态导入后,同一个类中有相同的方法的时候,会优先走自己定义的方法。 15 public static int round(double a){ 16 return 1000; 17 } 18 }
三大特性
封装(Encapsulation)
【1】生活案例:
ATM , 电线
【2】Java中封装的理解:
将某些东西进行隐藏,然后提供相应的方式进行获取。
我们程序设计追求“高内聚,低耦合”。
➢高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
➢低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提
高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露
的暴露出来。这就是封装性的设计思想。
【3】封装的好处:
提高代码的安全性
【4】代码:通过一个属性感受封装:
1 public class Girl {//女孩 2 //属性: 3 private int age; 4 //读取年龄: 5 public int duquAge(){ 6 return age; 7 } 8 //设置年龄: 9 public void shezhiAge(int age){ 10 if(age >= 30 ){ 11 this.age = 18; 12 }else{ 13 this.age = age; 14 } 15 } 16 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //创建一个Girl类的对象: 5 Girl g = new Girl(); 6 /*g.age = 33; 7 System.out.println(g.age);*/ 8 //设置年龄: 9 g.shezhiAge(31); 10 //读取年龄: 11 System.out.println(g.duquAge()); 12 } 13 }
上面的代码,对于属性age来说,我加了修饰符private,这样外界对它的访问就受到了限制,现在我还想加上其他的限制条件,但是在属性本身上没有办法再加了,所以我们通过定义方法来进行限制条件的添加。
以属性为案例:
进行封装:
(1)将属性私有化,被private修饰--》加入权限修饰符
一旦加入了权限修饰符,其他人就不可以随意的获取这个属性
(2)提供public修饰的方法让别人来访问/使用
(3)即使外界可以通过方法来访问属性了,但是也不能随意访问,因为咱们在方法中可以加入 限制条件。
【5】实际开发中,方法一般会写成 setter,getter方法:
可以利用IDEA快捷键生成:alt+insert -->getter and setter:
1 public class Girl {//女孩 2 //属性: 3 private int age; 4 //读取年龄: 5 public int getAge(){ 6 return age; 7 } 8 //设置年龄: 9 public void setAge(int age){ 10 if(age >= 30 ){ 11 this.age = 18; 12 }else{ 13 this.age = age; 14 } 15 } 16 }
【6】加深练习:
1 public class Student { 2 //属性: 3 private int age; 4 private String name; 5 private String sex; 6 //加入对应的setter和getter方法: 7 public int getAge() { 8 return age; 9 } 10 public void setAge(int age) { 11 this.age = age; 12 } 13 public String getName() { 14 return name; 15 } 16 public void setName(String name) { 17 this.name = name; 18 } 19 public String getSex() { 20 return sex; 21 } 22 public void setSex(String sex) { 23 if("男".equals(sex) || "女".equals(sex) ){//sex是男 或者 是 女 24 this.sex = sex; 25 }else{ 26 this.sex = "男"; 27 } 28 } 29 //加入构造器: 30 public Student(){ 31 } 32 public Student(int age,String name,String sex){ 33 this.age = age; 34 this.name = name; 35 //this.sex = sex; 36 this.setSex(sex); 37 } 38 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //创建一个Student对象: 5 Student s1 = new Student(); 6 s1.setName("nana"); 7 s1.setAge(19); 8 s1.setSex("女"); 9 System.out.println(s1.getName()+"---"+s1.getAge()+"----"+s1.getSex()); 10 Student s2 = new Student(18,"菲菲","asdfasdfsadf"); 11 System.out.println(s2.getName()+"---"+s2.getAge()+"----"+s2.getSex()); 12 } 13 }
继承(Inheritance)
【1】类是对对象的抽象:
举例:
荣耀20 ,小米 红米3,华为 p40 pro ---> 类:手机类
【2】继承是对类的抽象:
举例:
学生类:Student:
属性:姓名,年龄,身高,学生编号
方法:吃饭,睡觉,喊叫,学习
教师类:Teacher:
属性:姓名,年龄,身高,教师编号
方法:吃饭,睡觉,喊叫,教学
员工类:Emploee:
属性:姓名,年龄,身高,员工编号
方法:吃饭,睡觉,喊叫,工作
共同的东西:
人类:
属性:姓名,年龄,身高
方法:吃饭,睡觉,喊叫
学生类/教师类/员工类 继承 自 人类
以后定义代码:
先定义人类:
人类: ---》父类,基类,超类
属性:姓名,年龄,身高
方法:吃饭,睡觉,喊叫
再定义 : ---》子类,派生类
学生类:Student:
属性:学生编号
方法:学习
教师类:Teacher:
属性:教师编号
方法:教学
员工类:Emploee:
属性:员工编号
方法:工作
子类 继承自 父类
狗类:
属性:姓名,年龄,身高
方法:吃饭,睡觉,喊叫
我们的继承关系,是在合理的范围中进行的抽取 ,抽取出子类父类的关系:
上面的案例中:
学生类/教师类/员工类 继承 自 人类 ---》合理
学生类/教师类/员工类 继承 自 狗类 ---》不合理
区分:
学生是一个人
教师是一个人
员工是一个人 ---》合理
学生是一个狗 ---》不合理
总结:继承 就是 is - a 的关系
【3】代码层面的解释:
先写父类,再写子类:
父类:人类 Person
子类:学生类 Student
1 public class Person { 2 //属性: 3 private int age; 4 private String name; 5 private double height; 6 //提供setter getter方法: 7 public int getAge() { 8 return age; 9 } 10 public void setAge(int age) { 11 this.age = age; 12 } 13 public String getName() { 14 return name; 15 } 16 public void setName(String name) { 17 this.name = name; 18 } 19 public double getHeight() { 20 return height; 21 } 22 public void setHeight(double height) { 23 this.height = height; 24 } 25 //方法: 26 public void eat(){ 27 System.out.println("可以吃饭。。。"); 28 } 29 public void sleep(){ 30 System.out.println("可以睡觉。。。"); 31 } 32 }
1 public class Student extends Person {//子类Student 继承 父类Person 2 //属性: 3 private int sno;//学号 4 public int getSno() { 5 return sno; 6 } 7 public void setSno(int sno) { 8 this.sno = sno; 9 } 10 //方法: 11 public void study(){ 12 System.out.println("学生可以学习"); 13 } 14 }
1 public class Student extends Person {//子类Student 继承 父类Person 2 //属性: 3 private int sno;//学号 4 public int getSno() { 5 return sno; 6 } 7 public void setSno(int sno) { 8 this.sno = sno; 9 } 10 //方法: 11 public void study(){ 12 System.out.println("学生可以学习"); 13 } 14 }
【4】继承的好处:提高代码的复用性
父类定义的内容,子类可以直接拿过来用就可以了,不用代码上反复重复定义了
需要注意的点:
父类private修饰的内容,子类实际上也继承,只是因为封装的特性阻碍了直接调用,但是提供了间接调用的方式,可以间接调用。
【5】总结:
(1)继承关系 :
父类/基类/超类
子类/派生类
子类继承父类一定在合理的范围进行继承的 子类 extends 父类
(2)继承的好处:
1.提高了代码的复用性,父类定义的内容,子类可以直接拿过来用就可以了,不用代码上反复重复定义了
2.便于代码的扩展
3.为了以后多态的使用。是多态的前提。
(3)父类private修饰的内容,子类也继承过来了。
(4)一个父类可以有多个子类。
(5)一个子类只能有一个直接父类。
但是可以间接的继承自其它类。
(6)继承具有传递性:
Student --》继承自 Person ---》继承自Object
Object类是所有类的根基父类。
所有的类都直接或者间接的继承自Object。
内存分析
权限修饰符
【1】private:权限:在当前类中可以访问
【2】default:缺省修饰符:权限:到同一个包下的其他类都可以访问
【3】protected:权限:最大到不同包下的子类
【4】public:在整个项目中都可以访问
总结:
属性,方法:修饰符:四种:private,缺省,protected,public
类:修饰符:两种:缺省,public
以后写代码
一般属性:用private修饰 ,方法:用public修饰
方法的重写
【1】重写:
发生在子类和父类中,当子类对父类提供的方法不满意的时候,要对父类的方法进行重写。
【2】重写有严格的格式要求:
子类的方法名字和父类必须一致,参数列表(个数,类型,顺序)也要和父类一致。
【3】代码:
1 public class Person { 2 public void eat(){ 3 System.out.println("吃食物"); 4 } 5 public void sleep(){ 6 System.out.println("睡觉"); 7 } 8 }
1 public class Student extends Person { 2 public void study(){ 3 System.out.println("学习"); 4 } 5 @override 6 public void eat(){ 7 System.out.println("我喜欢吃小龙虾喝啤酒。。"); 8 } 9 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //创建一个Student类的对象: 5 Student s = new Student(); 6 s.eat(); 7 } 8 }
【4】内存:
【5】重载和重写的区别:
重载:在同一个类中,当方法名相同,形参列表不同的时候 多个方法构成了重载
重写:在不同的类中,子类对父类提供的方法不满意的时候,要对父类的方法进行重写。
super
【1】super:指的是: 父类的
【2】super可以修饰属性,可以修饰方法;
在子类的方法中,可以通过 super.属性 super.方法 的方式,显示的去调用父类提供的属性,方法。在通常情况下,super.可以省略不写:
在特殊情况下,当子类和父类的属性重名时,你要想使用父类的属性,必须加上修饰符super.,只能通过super.属性来调用
在特殊情况下,当子类和父类的方法重名时,你要想使用父类的方法,必须加上修饰符super.,只能通过super.方法来调用
在这种情况下,super.就不可以省略不写。
【3】super修饰构造器:
其实我们平时写的构造器的第一行都有:super() -->作用:调用父类的空构造器,只是我们一般都省略不写
(所有构造器的第一行默认情况下都有super(),但是一旦你的构造器中显示的使用super调用了父类构造器,那么这个super()就不会给你默认分配了。如果构造器中没有显示的调用父类构造器的话,那么第一行都有super(),可以省略不写)
如果构造器中已经显示的调用super父类构造器,那么它的第一行就没有默认分配的super();了
在构造器中,super调用父类构造器和this调用子类构造器只能存在一个,两者不能共存:
因为super修饰构造器要放在第一行,this修饰构造器也要放在第一行:
改正二选一即可:
【4】以后写代码构造器的生成可以直接使用IDEA提供的快捷键:
alt+insert
继承条件下构造方法的执行过程
1 public class Person { 2 int age; 3 String name; 4 public Person(int age, String name) { 5 super(); 6 this.age = age; 7 this.name = name; 8 } 9 public Person() { 10 } 11 }
1 public class Student extends Person { 2 double height ; 3 public Student() { 4 } 5 public Student(int age, String name, double height) { 6 super(age, name); 7 this.height = height; 8 } 9 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 Student s = new Student(19,"feifei",160.8); 5 } 6 }
Object类
所有类都直接或间接的继承自Object类,Object类是所有Java类的根基类。
也就意味着所有的Java对象都拥有Object类的属性和方法。
如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。
toString()方法
【1】Object类的toString()的作用:
方法的原理:
现在,使用toString方法的时候,打印出来的东西 “不好看”,对于其他人来说不友好,可读性不好
我们现在是想知道对象的信息,名字,年龄,身高。。。。。。
现在的格式不好:
出现的问题:子类Student对父类Object提供的toString方法不满意,不满意--》对toString方法进行重写:
1 public class Student /*extends Object*/{ 2 private String name; 3 private int age; 4 private double height; 5 public String getName() { 6 return name; 7 } 8 public void setName(String name) { 9 this.name = name; 10 } 11 public int getAge() { 12 return age; 13 } 14 public void setAge(int age) { 15 this.age = age; 16 } 17 public double getHeight() { 18 return height; 19 } 20 public void setHeight(double height) { 21 this.height = height; 22 } 23 public Student() { 24 } 25 public Student(String name, int age, double height) { 26 this.name = name; 27 this.age = age; 28 this.height = height; 29 } 30 public String toString() { 31 return "这是一个Student对象,这个对象的名字:"+name+",年龄:"+age+",身高:"+height; 32 } 33 }
测试类:
总结:toString的作用就是对对象进行“自我介绍”,一般子类对父类提供的toString都不满意,都要进行重写。
IDEA提供了快捷键:
1 public class Student /*extends Object*/{ 2 private String name; 3 private int age; 4 private double height; 5 public String getName() { 6 return name; 7 } 8 public void setName(String name) { 9 this.name = name; 10 } 11 public int getAge() { 12 return age; 13 } 14 public void setAge(int age) { 15 this.age = age; 16 } 17 public double getHeight() { 18 return height; 19 } 20 public void setHeight(double height) { 21 this.height = height; 22 } 23 public Student() { 24 } 25 public Student(String name, int age, double height) { 26 this.name = name; 27 this.age = age; 28 this.height = height; 29 } 30 /*public String toString() { 31 return "这是一个Student对象,这个对象的名字:"+name+",年龄:"+age+",身高:"+height; 32 }*/ 33 @Override 34 public String toString() { 35 return "Student{" + 36 "name='" + name + '\'' + 37 ", age=" + age + 38 ", height=" + height + 39 '}'; 40 } 41 }
equals方法
1 public class Phone {//手机类: 2 //属性: 3 private String brand;//品牌型号 4 private double price;//价格 5 private int year ;//出产年份 6 //方法: 7 public String getBrand() { 8 return brand; 9 } 10 public void setBrand(String brand) { 11 this.brand = brand; 12 } 13 public double getPrice() { 14 return price; 15 } 16 public void setPrice(double price) { 17 this.price = price; 18 } 19 public int getYear() { 20 return year; 21 } 22 public void setYear(int year) { 23 this.year = year; 24 } 25 @Override 26 public String toString() { 27 return "Phone{" + 28 "brand='" + brand + '\'' + 29 ", price=" + price + 30 ", year=" + year + 31 '}'; 32 } 33 //构造器: 34 public Phone() { 35 } 36 public Phone(String brand, double price, int year) { 37 this.brand = brand; 38 this.price = price; 39 this.year = year; 40 } 41 //对equals方法进行重写: 42 public boolean equals(Object obj) {//Object obj = new Phone(); 43 //将obj转为Phone类型: 44 Phone other = (Phone)obj;//向下转型,为了获取子类中特有的内容 45 if(this.getBrand()==other.getBrand()&&this.getPrice()==other.getPrice()&&this.getYear()==other.getYear()){ 46 return true; 47 } 48 return false; 49 } 50 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //创建Phone类的对象: 5 Phone p1 = new Phone("华为P40",2035.98,2020); 6 Phone p2 = new Phone("华为P40",2035.98,2020); 7 //比较两个对象:p1和p2对象: 8 //==的作用:比较左右两侧的值是否想的,要么相等,返回true,要么不相等,返回false 9 System.out.println(p1==p2);//-->>>对于引用数据类型来说,比较的是地址值。--->一定返回的是false 10 //Object类提供了一个方法 equals方法 :作用:比较对象具体内容是否相等。 11 boolean flag = p1.equals(p2);//点进源码发现:底层依旧比较的是==,比较的还是地址值。 12 System.out.println(flag); 13 } 14 }
总结:
equals作用:这个方法提供了对对象的内容是否相等 的一个比较方式,对象的内容指的就是属性。
父类Object提供的equals就是在比较==地址,没有实际的意义,我们一般不会直接使用父类提供的方法,
而是在子类中对这个方法进行重写。
instanceof
利用集成开发工具生成equals方法
【1】利用eclipse:
【2】利用idea:
类和类的关系
代码
总结:
【1】面向对象的思维:找参与者,找女孩类,找男孩类
【2】体会了什么叫方法的性擦,什么叫方法的实参:
具体传入的内容 实参:
【3】类和类可以产生关系:
(1)将一个类作为另一个类中的方法的形参
(2)将一个类作为另一个类的属性
1 public class Girl { 2 //属性: 3 String name; 4 double weight; 5 Mom m /*= new Mom()*/; 6 //方法: 7 public void add(int a){//参数是基本数据类型 8 System.out.println(a); 9 System.out.println(a+100); 10 } 11 //谈恋爱的方法: 12 public void love(Boy b){//参数是引用数据类型Boy 13 System.out.println("我男朋友的名字是:"+b.name+",我男朋友的年龄是:"+b.age); 14 b.buy(); 15 } 16 //女孩跟妈妈聊天: 17 public void wechat(){ 18 m.say(); 19 } 20 //构造器: 21 public Girl(String name, double weight) { 22 this.name = name; 23 this.weight = weight; 24 } 25 }
1 public class Boy { 2 //属性: 3 int age; 4 String name; 5 //方法: 6 public void buy(){ 7 System.out.println("跟我谈恋爱,我给你买买买。。。"); 8 } 9 //构造器: 10 public Boy(int age, String name) { 11 this.age = age; 12 this.name = name; 13 } 14 }
1 public class Mom { 2 //方法: 3 public void say(){ 4 System.out.println("妈妈唠唠叨叨 都是爱,听妈妈的话。。"); 5 } 6 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //创建一个Boy类的具体的对象: 5 Boy boy = new Boy(30,"鹿晗"); 6 //创建一个Girl类的具体的对象: 7 Girl girl = new Girl("关晓彤",100); 8 //谈恋爱: 9 //girl.love(boy); 10 Boy boy2 = new Boy(35,"陈伟霆"); 11 girl.love(boy2); 12 //还可以跟妈妈微信聊天: 13 girl.m = new Mom(); 14 girl.wechat(); 15 } 16 }
总结
一、继承关系
继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。在Java中继承关系通过关键字extends明确标识,在设计时一般没有争议性。在UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类,或者子接口指向父接口。
二、实现关系
实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系。在Java中此类关系通过关键字implements明确标识,在设计时一般没有争议性。在UML类图设计中,实现用一条带空心三角箭头的虚线表示,从类指向实现的接口。
三、依赖关系
简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖。表现在代码层面,让类B作为参数被类A在某个method方法中使用。在UML类图设计中,依赖关系用由类A指向类B的带箭头虚线表示。
四、关联关系
关联体现的是两个类之间语义级别的一种强依赖关系,比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的。关联可以是单向、双向的。表现在代码层面,为被关联类B以类的属性形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量。在UML类图设计中,关联关系用由关联类A指向被关联类B的带箭头实线表示,在关联的两端可以标注关联双方的角色和多重性标记。
五、聚合关系
聚合是关联关系的一种特例,它体现的是整体与部分的关系,即has-a的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。比如计算机与CPU、公司与员工的关系等,比如一个航母编队包括海空母舰、驱护舰艇、舰载飞机及核动力攻击潜艇等。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,聚合关系以空心菱形加实线箭头表示。
六、组合关系
组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束,比如人和人的大脑。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,组合关系以实心菱形加实线箭头表示。
七、总结
对于继承、实现这两种关系没多少疑问,它们体现的是一种类和类、或者类与接口间的纵向关系。其他的四种关系体现的是类和类、或者类与接口间的引用、横向关系,是比较难区分的,有很多事物间的关系要想准确定位是很难的。前面也提到,这四种关系都是语义级别的,所以从代码层面并不能完全区分各种关系,但总的来说,后几种关系所表现的强弱程度依次为:组合>聚合>关联>依赖。
多态(Polymorphism)
【1】多态跟属性无关,多态指的是方法的多态,而不是属性的多态。
【2】案例代入:
1 public class Animal {//父类:动物: 2 public void shout(){ 3 System.out.println("我是小动物,我可以叫。。。"); 4 } 5 }
1 public class Cat extends Animal{ 2 //喊叫方法: 3 public void shout(){ 4 System.out.println("我是小猫,可以喵喵叫"); 5 } 6 public void scratch(){ 7 System.out.println("我是小猫,我可以挠人"); 8 } 9 }
1 public class Dog extends Animal{ 2 //喊叫: 3 public void shout(){ 4 System.out.println("我是小狗,我可以汪汪叫"); 5 } 6 public void guard(){ 7 System.out.println("我是小狗,我可以看家护院,保护我的小主人。。。"); 8 } 9 }
1 public class Pig extends Animal{ 2 public void shout(){ 3 System.out.println("我是小猪,我嗯嗯嗯的叫"); 4 } 5 public void eat(){ 6 System.out.println("我是小猪,我爱吃东西。。"); 7 } 8 }
1 public class Girl { 2 //跟猫玩耍: 3 /*public void play(Cat cat){ 4 cat.shout(); 5 }*/ 6 //跟狗玩耍: 7 /*public void play(Dog dog){ 8 dog.shout(); 9 }*/ 10 //跟小动物玩耍: 11 public void play(Animal an){ 12 an.shout(); 13 } 14 }
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //具体的猫:--》猫的对象 5 //Cat c = new Cat(); 6 //具体的小女孩:--》女孩的对象 7 Girl g = new Girl(); 8 //小女孩跟猫玩: 9 //g.play(c); 10 //具体的狗---》狗的对象: 11 //Dog d = new Dog(); 12 //小女孩跟狗玩: 13 //g.play(d); 14 //具体的动物:--》动物的对象: 15 //Cat c = new Cat(); 16 //Dog d = new Dog(); 17 Pig p = new Pig(); 18 Animal an = p; 19 g.play(an); 20 } 21 }
【3】总结:
(1)先有父类,再有子类:--》继承 先有子类,再抽取父类 ----》泛化
(2)什么是多态:
多态就是多种状态:同一个行为,不同的子类表现出来不同的形态。
多态指的就是同一个方法调用,然后由于对象不同会产生不同的行为。
(3)多态的好处:
为了提高代码的扩展性,符合面向对象的设计原则:开闭原则。
开闭原则:指的就是扩展是 开放的,修改是关闭的。
注意:多态可以提高扩展性,但是扩展性没有达到最好,以后我们会学习 反射
(4)多态的要素:
一,继承: Cat extends Animal ,Pig extends Animal, Dog extends Animal
二,重写:子类对父类的方法shout()重写
三, 父类引用指向子类对象:
1 Pig p = new Pig(); 2 Animal an = p;
将上面的代码合为一句话:
Animal an = new Pig();
=左侧:编译期的类型
=右侧:运行期的类型
Animal an = new Pig();
g.play(an); //
1 public void play(Animal an){//Animal an = an = new Pig(); 2 an.shout(); 3 }
上面的代码,也是多态的一种非常常见的应用场合:父类当方法的形参,然后传入的是具体的子类的对象,
然后调用同一个方法,根据传入的子类的不同展现出来的效果也不同,构成了多态。
内存分析
向下转型,向上转型
现在我就想访问到eat()方法和weight属性:
1 public class Demo { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 Pig p = new Pig(); 5 Animal an = p;//转型:向上转型 6 an.shout(); 7 //加入转型的代码: 8 //将Animal转为Pig类型: 9 Pig pig = (Pig)an ;//转型:向下转型 10 pig.eat(); 11 pig.age = 10; 12 pig.weight = 60.8; 13 } 14 }
对应内存:
思考之前的equals方法:
简单工厂设计模式
不仅可以使用父类做方法的形参,还可以使用父类做方法的返回值类型,真实返回的对象可以是该类的任意一个子类对象。
简单工厂模式的实现,它是解决大量对象创建问题的一个解决方案。将创建和使用分开,工厂负责创建,使用者直接调用即可。简单工厂模式的基本要求是
定义一个static方法,通过类名直接调用
返回值类型是父类类型,返回的可以是其任意子类类型
传入一个字符串类型的参数,工厂根据参数创建对应的子类产品
1 public class Test { 2 public static void main(String[] args) { 3 Girl g = new Girl(); 4 //Cat c = new Cat(); 5 //Dog d = new Dog(); 6 //Pig p = new Pig(); 7 Animal an = PetStore.getAnimal("狗"); 8 g.play(an); 9 } 10 }
1 public class PetStore {//宠物店 ---》工厂类 2 //方法:提供动物 3 public static Animal getAnimal(String petName){//多态的应用场合(二) 4 Animal an = null; 5 if("猫".equals(petName)){//petName.equals("猫") --》这样写容易发生空指针异常 6 an = new Cat(); 7 } 8 if("狗".equals(petName)){ 9 an = new Dog(); 10 } 11 if("猪".equals(petName)){ 12 an = new Pig(); 13 } 14 return an; 15 } 16 }
final
【1】修饰变量;
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //第1种情况: 5 //final修饰一个变量,变量的值不可以改变,这个变量也变成了一个字符常量,约定俗称的规定:名字大写 6 final int A = 10;//final修饰基本数据类型 7 //A = 20; 报错:不可以修改值 8 //第2种情况: 9 final Dog d = new Dog();//final修饰引用数据类型,那么地址值就不可以改变 10 //d = new Dog(); -->地址值不可以更改 11 //d对象的属性依然可以改变: 12 d.age = 10; 13 d.weight = 13.7; 14 //第3种情况: 15 final Dog d2 = new Dog(); 16 a(d2); 17 //第4种情况: 18 b(d2); 19 } 20 public static void a(Dog d){ 21 d = new Dog(); 22 } 23 public static void b(final Dog d){//d被final修饰 ,指向不可以改变 24 //d = new Dog(); 25 } 26 }
【2】修饰方法;
final修饰方法,那么这个方法不可以被该类的子类重写:
【3】修饰类;
final修饰类,代表没有子类,该类不可以被继承:
一旦一个类被final修饰,那么里面的方法也没有必要用final修饰了(final可以省略不写)
【4】案例:JDK提供的Math类:看源码发现:
(1)使用Math类的时候无需导包,直接使用即可:
(2)Math类没有子类,不能被其他类继承了
(3)里面的属性全部被final修饰,方法也是被final修饰的,只是省略不写了
原因:子类没有必要进行重写。
(4)外界不可以创建对象:
Math m = new Math();
(5)发现Math类中的所有的属性,方法都被static修饰
那么不用创建对象去调用,只能通过类名.属性名 类名.方法名 去调用
抽象类,抽象方法
【1】抽象类和抽象方法的关系:
抽象类中可以定义0-n个抽象方法。
【2】抽象类作用:
在抽象类中定义抽象方法,目的是为了为子类提供一个通用的模板,子类可以在模板的基础上进行开发,先重写父类的抽象方法,然后可以扩展子类自己的内容。抽象类设计避免了子类设计的随意性,通过抽象类,子类的设计变得更加严格,进行某些程度上的限制。
使子类更加的通用。
【3】代码:
1 //4.一个类中如果有方法是抽象方法,那么这个类也要变成一个抽象类。 2 //5.一个抽象类中可以有0-n个抽象方法 3 public abstract class Person { 4 //1.在一个类中,会有一类方法,子类对这个方法非常满意,无需重写,直接使用 5 public void eat(){ 6 System.out.println("一顿不吃饿得慌"); 7 } 8 //2.在一个类中,会有一类方法,子类对这个方法永远不满意,会对这个方法进行重写。 9 //3.一个方法的方法体去掉,然后被abstract修饰,那么这个方法就变成了一个抽象方法 10 public abstract void say(); 11 public abstract void sleep(); 12 } 13 //6.抽象类可以被其他类继承: 14 //7.一个类继承一个抽象类,那么这个类可以变成抽象类 15 //8.一般子类不会加abstract修饰,一般会让子类重写父类中的抽象方法 16 //9.子类继承抽象类,就必须重写全部的抽象方法 17 //10.子类如果没有重写父类全部的抽象方法,那么子类也可以变成一个抽象类。 18 class Student extends Person{ 19 @Override 20 public void say() { 21 System.out.println("我是东北人,我喜欢说东北话。。"); 22 } 23 @Override 24 public void sleep() { 25 System.out.println("东北人喜欢睡炕。。"); 26 } 27 } 28 class Demo{ 29 //这是一个main方法,是程序的入口: 30 public static void main(String[] args) { 31 //11.创建抽象类的对象:-->抽象类不可以创建对象 32 //Person p = new Person(); 33 //12.创建子类对象: 34 Student s = new Student(); 35 s.sleep(); 36 s.say(); 37 38 //13.多态的写法:父类引用只想子类对象: 39 Person p = new Student(); 40 p.say(); 41 p.sleep(); 42 } 43 }
【4】面试题:
(1)抽象类不能创建对象,那么抽象类中是否有构造器?
抽象类中一定有构造器。构造器的作用 给子类初始化对象的时候要先super调用父类的构造器。
(2)抽象类是否可以被final修饰?
不能被final修饰,因为抽象类设计的初衷就是给子类继承用的。要是被final修饰了这个抽象类了,就不存在继承了,就没有子类。
接口
【1】接口声明格式:
[访问修饰符] interface 接口名 [extends 父接口1,父接口2…] { 常量定义; 方法定义; } |
【2】代码:
1 /** 2 * 1.类是类,接口是接口,它们是同一层次的概念。 3 * 2.接口中没有构造器 4 * 3.接口如何声明:interface 5 * 4.在JDK1.8之前,接口中只有两部分内容: 6 * (1)常量:固定修饰符:public static final 7 * (2)抽象方法:固定修饰符:public abstract 8 * 注意:修饰符可以省略不写,IDE会帮你自动补全,但是初学者建议写上,防止遗忘。 9 */ 10 public interface TestInterface01 { 11 //常量: 12 /*public static final*/ int NUM = 10; 13 //抽象方法: 14 /*public abstract*/ void a(); 15 /*public abstract*/ void b(int num); 16 /*public abstract*/ int c(String name); 17 } 18 interface TestInterface02{ 19 void e(); 20 void f(); 21 } 22 /* 23 5.类和接口的关系是什么? 实现关系 类实现接口: 24 6.一旦实现一个接口,那么实现类要重写接口中的全部的抽象方法: 25 7.如果没有全部重写抽象方法,那么这个类可以变成一个抽象类。 26 8.java只有单继承,java还有多实现 27 一个类继承其他类,只能直接继承一个父类 28 但是实现类实现接口的话,可以实现多个接口 29 9.写法:先继承 再实现:extends Person implements TestInterface01,TestInterface02 30 */ 31 class Student extends Person implements TestInterface01,TestInterface02 { 32 @Override 33 public void a() { 34 System.out.println("---1"); 35 } 36 @Override 37 public void b(int num) { 38 System.out.println("---2"); 39 } 40 @Override 41 public int c(String name) { 42 return 100; 43 } 44 @Override 45 public void e() { 46 System.out.println("---3"); 47 } 48 @Override 49 public void f() { 50 System.out.println("---4"); 51 } 52 } 53 class Test{ 54 //这是一个main方法,是程序的入口: 55 public static void main(String[] args) { 56 //10.接口不能创建对象: 57 //TestInterface02 t = new TestInterface02(); 58 TestInterface02 t = new Student();//接口指向实现类 ---》多态 59 //11.接口中常量如何访问: 60 System.out.println(TestInterface01.NUM); 61 System.out.println(Student.NUM); 62 Student s = new Student(); 63 System.out.println(s.NUM); 64 TestInterface01 t2 = new Student(); 65 System.out.println(t2.NUM); 66 } 67 }
【3】接口的作用是什么?
定义规则,只是跟抽象类不同地方在哪?它是接口不是类。
接口定义好规则之后,实现类负责实现即可。
【4】
继承:子类对父类的继承
实现:实现类对接口的实现
手机 是不是 照相机
继承:手机 extends 照相机 “is-a”的关系,手机是一个照相机
上面的写法 不好:
实现: 手机 implements 拍照功能 “has-a”的关系,手机具备照相的能力
案例:飞机,小鸟,风筝
定义一个接口: Flyable
【5】多态的应用场合:
(1)父类当做方法的形参,传入具体的子类的对象
(2)父类当做方法的返回值,返回的是具体的子类的对象
(3)接口当做方法的形参,传入具体的实现类的对象
(4)接口当做方法的返回值,返回的是具体的实现类的对象
【6】接口和抽象类的区别:
JDK1.8以后的接口新增内容
在JDK1.8之前,接口中只有两部分内容:
(1)常量:固定修饰符:public static final
(2)抽象方法:固定修饰符:public
abstract
在JDK1.8之后,新增非抽象方法:
(1)被public default修饰的非抽象方法:
注意1:default修饰符必须要加上,否则出错
注意2:实现类中要是想重写接口中的非抽象方法,那么default修饰符必须不能加,否则出错。
1 public interface TestInterface { 2 //常量: 3 public static final int NUM= 10; 4 //抽象方法: 5 public abstract void a(); 6 //public default修饰的非抽象方法: 7 public default void b(){ 8 System.out.println("-------TestInterface---b()-----"); 9 } 10 } 11 class Test implements TestInterface{ 12 public void c(){ 13 //用一下接口中的b方法: 14 b();//可以 15 //super.b();不可以 16 TestInterface.super.b();//可以 17 } 18 @Override 19 public void a() { 20 System.out.println("重写了a方法"); 21 } 22 @Override 23 public void b() { 24 } 25 }
(2)静态方法:
注意1:static不可以省略不写
注意2:静态方法不能重写
1 public interface TestInterface2 { 2 //常量: 3 public static final int NUM = 10; 4 //抽象方法: 5 public abstract void a(); 6 //public default非抽象方法; 7 public default void b(){ 8 System.out.println("-----TestInterface2---b"); 9 } 10 //静态方法: 11 public static void c(){ 12 System.out.println("TestInterface2中的静态方法"); 13 } 14 } 15 class Demo implements TestInterface2{ 16 @Override 17 public void a() { 18 System.out.println("重写了a方法"); 19 } 20 public static void c(){ 21 System.out.println("Demo中的静态方法"); 22 } 23 } 24 class A { 25 //这是一个main方法,是程序的入口: 26 public static void main(String[] args) { 27 Demo d = new Demo(); 28 d.c(); 29 Demo.c(); 30 TestInterface2.c(); 31 } 32 }
疑问:为什么要在接口中加入非抽象方法???
如果接口中只能定义抽象方法的话,那么我要是修改接口中的内容,那么对实现类的影响太大了,所有实现类都会受到影响。
现在在接口中加入非抽象方法,对实现类没有影响,想调用就去调用即可。
内部类
成员内部类
1 /** 2 * 1.类的组成:属性,方法,构造器,代码块(普通块,静态块,构造块,同步块),内部类 3 * 2.一个类TestOuter的内部的类SubTest叫内部类, 内部类 :SubTest 外部类:TestOuter 4 * 3.内部类:成员内部类 (静态的,非静态的) 和 局部内部类(位置:方法内,块内,构造器内) 5 * 4.成员内部类: 6 * 里面属性,方法,构造器等 7 * 修饰符:private,default,protect,public,final,abstract 8 */ 9 public class TestOuter { 10 //非静态的成员内部类: 11 public class D{ 12 int age = 20; 13 String name; 14 public void method(){ 15 //5.内部类可以访问外部类的内容 16 /*System.out.println(age); 17 a();*/ 18 int age = 30; 19 //8.内部类和外部类属性重名的时候,如何进行调用: 20 System.out.println(age);//30 21 System.out.println(this.age);//20 22 System.out.println(TestOuter.this.age);//10 23 } 24 } 25 //静态成员内部类: 26 static class E{ 27 public void method(){ 28 //6.静态内部类中只能访问外部类中被static修饰的内容 29 /*System.out.println(age); 30 a();*/ 31 } 32 } 33 //属性: 34 int age = 10; 35 //方法: 36 public void a(){ 37 System.out.println("这是a方法"); 38 { 39 System.out.println("这是一个普通块"); 40 class B{ 41 } 42 } 43 class A{ 44 } 45 //7.外部类想要访问内部类的东西,需要创建内部类的对象然后进行调用 46 D d = new D(); 47 System.out.println(d.name); 48 d.method(); 49 } 50 static{ 51 System.out.println("这是静态块"); 52 } 53 { 54 System.out.println("这是构造块"); 55 } 56 //构造器: 57 public TestOuter(){ 58 class C{ 59 } 60 } 61 public TestOuter(int age) { 62 this.age = age; 63 } 64 } 65 class Demo{ 66 //这是一个main方法,是程序的入口: 67 public static void main(String[] args) { 68 //创建外部类的对象: 69 TestOuter to = new TestOuter(); 70 to.a(); 71 //9.创建内部类的对象: 72 //静态的成员内部类创建对象: 73 TestOuter.E e = new TestOuter.E(); 74 //非静态的成员内部类创建对象: 75 //错误:TestOuter.D d = new TestOuter.D(); 76 TestOuter t = new TestOuter(); 77 TestOuter.D d = t.new D(); 78 } 79 }
局部内部类
1 public class TestOuter { 2 //1.在局部内部类中访问到的变量必须是被final修饰的 3 public void method(){ 4 final int num = 10; 5 class A{ 6 public void a(){ 7 //num = 20; 8 System.out.println(num); 9 } 10 } 11 } 12 //2.如果类B在整个项目中只使用一次,那么就没有必要单独创建一个B类,使用内部类就可以了 13 public Comparable method2(){ 14 class B implements Comparable{ 15 @Override 16 public int compareTo(Object o) { 17 return 100; 18 } 19 } 20 return new B(); 21 } 22 public Comparable method3(){ 23 //3.匿名内部类 24 return new Comparable(){ 25 @Override 26 public int compareTo(Object o) { 27 return 200; 28 } 29 }; 30 } 31 public void teat(){ 32 Comparable com = new Comparable(){ 33 @Override 34 public int compareTo(Object o) { 35 return 200; 36 } 37 }; 38 System.out.println(com.compareTo("abc")); 39 } 40 }
面向对象项目
项目需求
项目结构分析
最终代码
匹萨父类:
1 public class Pizza { 2 //属性 3 private String name;//名称 4 private int size;//大小 5 private int price;//价格 6 //方法 7 public String getName() { 8 return name; 9 } 10 public void setName(String name) { 11 this.name = name; 12 } 13 public int getSize() { 14 return size; 15 } 16 public void setSize(int size) { 17 this.size = size; 18 } 19 public int getPrice() { 20 return price; 21 } 22 public void setPrice(int price) { 23 this.price = price; 24 } 25 //展示匹萨信息: 26 public String showPizza(){ 27 return "匹萨的名字是:"+name+"\n匹萨的大小是:"+size+"寸\n匹萨的价格:"+price+"元"; 28 } 29 //构造器 30 public Pizza() { 31 } 32 public Pizza(String name, int size, int price) { 33 this.name = name; 34 this.size = size; 35 this.price = price; 36 } 37 }
培根匹萨:
1 public class BaconPizza extends Pizza { 2 //属性: 3 private int weight; 4 public int getWeight() { 5 return weight; 6 } 7 public void setWeight(int weight) { 8 this.weight = weight; 9 } 10 //构造器: 11 public BaconPizza() { 12 } 13 public BaconPizza(String name, int size, int price, int weight) { 14 super(name, size, price); 15 this.weight = weight; 16 } 17 //重写父类showPizza方法: 18 @Override 19 public String showPizza() { 20 return super.showPizza()+"\n培根的克数是:"+weight+"克"; 21 } 22 }
水果匹萨:
1 public class FruitsPizza extends Pizza{ 2 //属性: 3 private String burdening; 4 public String getBurdening() { 5 return burdening; 6 } 7 public void setBurdening(String burdening) { 8 this.burdening = burdening; 9 } 10 //构造器: 11 public FruitsPizza() { 12 } 13 public FruitsPizza(String name, int size, int price, String burdening) { 14 super(name, size, price); 15 this.burdening = burdening; 16 } 17 //重写父类showPizza方法: 18 @Override 19 public String showPizza() { 20 return super.showPizza()+"\n你要加入的水果:"+burdening; 21 } 22 }
测试类:
1 public class Test { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) { 4 //选择购买匹萨: 5 Scanner sc = new Scanner(System.in); 6 System.out.println("请选择你想要购买的匹萨(1.培根匹萨 2.水果匹萨):"); 7 int choice = sc.nextInt();//选择 8 //通过工厂获取匹萨: 9 Pizza pizza = PizzaStore.getPizza(choice); 10 System.out.println(pizza.showPizza()); 11 } 12 }
工厂类:
1 package com.llh; 2 3 import java.util.Scanner; 4 5 6 public class PizzaStore { 7 public static Pizza getPizza(int choice){ 8 Scanner sc = new Scanner(System.in); 9 Pizza p = null; 10 switch (choice){ 11 case 1: 12 { 13 System.out.println("请录入培根的克数:"); 14 int weight = sc.nextInt(); 15 System.out.println("请录入匹萨的大小:"); 16 int size = sc.nextInt(); 17 System.out.println("请录入匹萨的价格:"); 18 int price = sc.nextInt(); 19 //将录入的信息封装为培根匹萨的对象: 20 BaconPizza bp = new BaconPizza("培根匹萨",size,price,weight); 21 p = bp; 22 } 23 break; 24 case 2: 25 { 26 System.out.println("请录入你想要加入的水果:"); 27 String burdening = sc.next(); 28 System.out.println("请录入匹萨的大小:"); 29 int size = sc.nextInt(); 30 System.out.println("请录入匹萨的价格:"); 31 int price = sc.nextInt(); 32 //将录入的信息封装为水果匹萨的对象: 33 FruitsPizza fp = new FruitsPizza("水果匹萨",size,price,burdening); 34 p = fp; 35 } 36 break; 37 } 38 return p; 39 } 40 }
以上为面向对象的基本内容。