1 Day06–面向对象1
1.1 面向对象
1.1.1 概念
推荐看的书:Thinking in java
概念:所谓的面向对象是一种编程思想,通过这种思想可以把生活中的复杂事情变得简单化,从原来的执行者变成了指挥者,面向对象是基于面向过程而言的。
面向过程的结构化设计的优缺点:
1).缺点:缺乏对数据的封装。
2).缺点:数据与方法(操作数据的)难以分离。
3).优点:性能比较高。
面向对象设计的优缺点:
1).优点:易维护,易扩展,易复用。
2).缺点:性能比面向过程低。
面向过程强调的是过程,例如:
1、打开冰箱 2、把大象放进去 3、关上冰箱
面向对象强调结果,例如:
1、饿了,去平台点餐,这个动作就是面向对象。你没有去市场买菜洗菜做饭。。。只要有app就可以了。
2、衣服脏了,直接甩给女票去处理等着穿干净的就可以了。你没有关注中间的过程。。只要找好对象就可以了。
3、面试官问什么是面向对象?你答万物皆对象!!不建议因为你还没到这个深度,最好举例。就像是,你说空即是色色即是空—信你个鬼。
我们经常说的面向对象的编程实现(OOP,Object Oriented Programming)
面向对象的本质是:以类的方式组织代码,以对象的组织封装数据。
1.1.2 三大特征
1.封装: 封装实现了软件部件的“高内聚、低耦合”,防止程序因依赖而带来的变动影响。
(1)类:封装的是对象的属性和行为
(2)方法:封装的是特定的业务逻辑功能实现
(3)访问控制修饰符:封装的是具体的访问权限
2.继承: 继承提高了软件的可重用性和可扩展性。
(1)作用:便于代码的复用,减少代码冗余,提高程序可维护性和可扩展性
(2)超类:所有派生类所共有的属性和行为
接口:部分派生类所共有的行为
派生类:派生类所特有的属性和行为
(3)类与类直接是单继承,接口与接口之间是多继承,类与接口是多实现
3.多态: 多态增强了软件的灵活性和可扩展性。
(1)分类:
(1.1)行为的多态(可举例)
(1.2)对象的多态(可举例)
(2)向上造型、强制类型转换、instanceof判断
(3)多态的表现形式
(3.1)重写:根据对象的不同来表现多态
(3.2)重载:根据参数的不同来表现多态
1.1.3 扩展:值传递和引用传递
规范:一个项目只有应该只有一个主启动类
java是值传递。
package com.shuai;public class Demo01 {//值传递public static void main(String[] args) {int a = 1;System.out.println(a);//1change(a);System.out.println(a);//1 没有修改成功}public static void change(int a) {//返回值为空,就没有返回值a=10;}}
1.2 类和对象
1.2.1 类
1、Java语言最基本单位就是类,类似于类型。
2、类是一类事物的抽象。(类别/类型,代表一类个体)
3、可以理解为模板或者设计图纸。
4、同一个包中的类可以相互使用,但不能同名(必须保存后)。
5、语法 : class 类名{ }
6、类中一般包含:
所有对象所共有的属性/特征 --------成员变量所有对象所共有的行为 -------方法
同包中的类可以直接访问,不同包中的类不能直接访问,想访问:
先import声明一个全称的类(包名+类名),再访问类(就是new一个对象)类的全称访问-------太繁琐、不建议(声明一个写一个太麻烦)
注意:一个.java文件中可以有多个class,但是要求,被public修饰的类只能有一个,并且这个类名就是文件名(区分大小写,也可以没有一个public修饰的类)。不同的文件可以有多个public.褚提时可能是不同的文件写在一块所以可以写多个public
1.2.2 对象
对象:真实存在的单个的个体。
每个对象具有三个特点:对象的状态,对象的行为和对象的标识。
1、对象的状态用来描述对象的基本特征。
2、对象的行为用来描述对象的功能。
3、对象的标识是指对象在内存中都有一个唯一的地址用来和其他对象区分开来。
4、类是一类事物的抽象,对象是具体的实现。
5、语法: new 类名();
6、每次new都会创建一个新的对象。
7、使用new关键字创建对象分配空间后会干2件事:默认的初始化(给变量赋默认值)和调用构造方法。
调用成员变量和方法(创建对象时):
引用类型的名字.局部变量名 = 赋值;引用类型的名字.方法调用的方式;
为什么要创建对象:因为访问,访问就是访问类里面的东西,变量和方法,因为只有实例化之后,才能将这个对象放到内存中,然后才能在规定的范围内来调用
1.2.3 类和对象的关系
1、计算机语言是用来描述现实世界事物的。属性+行为
2、那怎么通过java语言描述呢?通过类来描述事物,把事物的属性当做成员变量
,把行为当做成员方法
。
3、一个类可以创建多个对象。
总结:类是对象的模板,对象是类的具体事例。
以手机举例:
属性:颜色,尺寸,品牌,价格。方法:打电话,发短信,听音乐。类:手机类,抽取相同的属性和行为对象:可以按照模板生产很多个手机,比如1号手机对象,包含特有的成员变量和成员方法
1.3 类和对象的创建和使用
1.3.1 练习1:类的创建使用
通过class关键字创建类,通过new关键字创建对象。
1).
public class Test1_CreateObject {public static void main(String[] args) {//创建对象测试//3,通过关键字new来创建对象//new Phone()匿名对象,一次只干一个活//new Phone().call(); 只能干一件事//4,p是Phone类型是引用类型的变量,引用了,,,内存中的地址值Phone p = new Phone();//引用类型的创建,引用类型的默认值是null,引用类型与它需要实例的对象类名一致。//p代表了Phone对象,真的能用模板定义的功能吗???//调用方法p.call();p.message();p.music();//设置属性值p.color="green"; //调用的时候改值p.size=6;p.pinpai="HUAWEI";p.price=20000;//调用属性System.out.println(p.color);//null --greenSystem.out.println(p.size);//0 -- 6System.out.println(p.pinpai);//null --HUAWEISystem.out.println(p.price);//0.0 --20000.0}}//1,创建手机类,用来描述手机事物//2,通常描述:事物的特征+事物的行为class Phone{// 事物的特征 -- 成员变量/成员属性// 特征:颜色,尺寸,品牌,价格String color;int size;String pinpai;double price;// 事物的行为 -- 成员方法/成员函数// 行为/功能:打电话,发短信,听音乐//修饰符 返回值 方法名(参数列表){方法体}public void call() {System.out.println("call()...");}public void message() {System.out.println("message()...");}public void music() {System.out.println("music()...");}}
2).
package cn.tedu.oop;//练习public class Test2_Car {public static void main(String[] args) {//2,创建Car对象测试Car c = new Car();//调用功能c.run();c.stop();//设置属性值c.color="red";c.model="BMW5";c.pinpai="BMW";//直接输出变量System.out.println(c.color);System.out.println(c.model);System.out.println(c.pinpai);}}//1,创建Car类,用来描述汽车事物class Car{// -- 特征+行为String color;String model;String pinpai;public void run() {System.out.println("正在飞");}public void stop() {System.out.println("停车");}}
1.3.2 对象在内存中的存储
Java把内存分成5大区域,内存由JVM管理的,我们重点关注栈和堆。
一般来讲局部变量存在栈中,方法执行完毕内存就被释放(栈比堆小)对象(new出来的东西)存在堆中,对象不再被使用时,内存才会被释放每个堆内存的元素都有地址值对象中的属性都是有默认值的地址值都是唯一的不会重重复(存地址值,占用内存空间开销小)栈是:先进后出原则(类似于子弹上膛) 堆:没有这个原则,放到哪去都可以。内存由JVM管理的:先加载类,在加载.class会在方法区分配,再分配堆,在分配栈(栈和堆几乎同时开始)对象完成初始化才有地址值栈:方法执行完后释放。堆:当没有任何引用时调用时释放内存。
栈,堆,方法区各存什么?
栈:局部变量(包括方法的参数,也叫局部变量),保存一个对应的对象的地址值 。
堆:new出来的对象(包括实例变量和方法)。
方法区:.class字节码文件(静态方法和静态变量)类里面,只被加载一次。
1.3.3 单一对象内存图
名词: 出栈
1.3.4 练习2:创建多对象
package cn.tedu.oop;//测试多个对象的创建和使用public class Test3_Person {public static void main(String[] args) {//创建对象测试 (addr:地址 brand:品牌)Person p = new Person();p.game(); //调用方法的时候可以传参赋值p.code();//设置属性值:在调用属性的时候赋值p.name="rose";p.age=20;p.gender=1;p.address="北京";//打印System.out.println(p.name);System.out.println(p.age);System.out.println(p.gender);System.out.println(p.address);/*如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。*/Person p2 = new Person();p2.code();p2.game();//TODO 设置属性值System.out.println(p2.name);//nullSystem.out.println(p2.age);System.out.println(p2.gender);System.out.println(p2.address);//p3是引用类型的变量,这里没有new,在堆内存中就不会开辟新空间。//p3保存了p2保存着的地址值Person p3 = p2;}}//创建Person类class Person{//特征String name;int age;int gender;//0男1女String address;//行为public void code() {System.out.println("敲代码");}public void game() {System.out.println("吃鸡");}}
1.3.5 多对象内存图
1、变量p和变量p2不是一片空间,p1需要开辟新的空间
2、Person p2=new Person,这时只要有new,就会新开辟空间在堆内存中存入对象。
1.4 封装
1.4.1 概述
封装是指隐藏对象的属性和实现细节,仅仅对外提供公共的访问方式。
好处:
1、提高安全性
2、提高重用性
案例:
1、类
2、方法
1.4.2 private关键字
是一个权限修饰符,用于修饰成员变量和成员函数,被私有化的成员只能在本类中访问。想要修改只能访问对外提供公共的get(获取值)和set(赋值)方法,之前直接可以通过对象.资源
来调用赋值,现在只能通过set 方法或者构造方法进行赋值操作,当然单单只是调用还是对象.资源
进行调用。
封装,利用private关键字实现,目的是提高安全性,本类。
比如:一个public int age.外界可以随意调用它赋值若为10000岁,显然不合常理。想要使用private修饰的变量只能通过访问public修饰的方法来进行调用,在方法中我们可以添加一些限制条件,而在变量中没法加。
一般封装 : 属性(成员变量)私有化 ,行为(方法)公开化。
1.4.3 练习1:封装学生
创建学生类,创建学生对象测试
package cn.tedu.oop;//测试封装public class Test4_Private {public static void main(String[] args) {//创建学生对象测试Student s = new Student();//调用功能// s.study();//由于study()被private修饰了,除了自己的类,别的类都用不了s.sleep();// System.out.println(s.name);//由于name被private修饰了,除了自己的类,别的类都用不了//3,如果我就是想要修改已经被private的name属性的值? --访问公共的set()// s.name ="jack";//无法访问s.setName("jack"); //调用set方法赋值88888888888//4,如果外界就是想要获取被private的name属性的值? --访问公共的get()// System.out.println(s.name) ;//无法访问System.out.println(s.getName());//和平常的输出不一样,这个是直接输出的方法。(因为get方法有返回值一般前边是定义一个变量来接受,所以说这个地方可以直接输出方法)}}//创建学生类class Student{//成员属性//1.1,封装:成员变量,封装好的变量,外界无法直接访问,需要间接访问公共的set()设置值,get()获取值 有快捷键:source------get and setprivate String name;//3.1,对外提供一个公共的修改方法 -- setXxx() 没有返回值public void setName(String n) {//拿到n之后,需要把n的值赋值给namename = n;} //4.1,对外提供一个公共的获取改方法 -- getXxx() 有返回值public String getName(){//4.2,通过return关键字,把name属性的值,返回给调用位置return name;}//TODO 封装以下三个属性,并提供set()/get()private String subject;public void setSubject(String s) {subject = s ;}public String getSubject() {return subject;}private int sno;private int age;//eclipse自动生成代码:右键-source-setters and getters-select all-okpublic int getSno() {return sno;}public void setSno(int sno) {this.sno = sno;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}//成员方法//1,封装:利用private关键字实现,目的就是提高代码的安全性,被private之后,资源只能在本类中看见private void study() {System.out.println("正在学习");}public void sleep() {//2,如果外界还是想执行study(),可以访问公共的sleep(),间接实现访问study() study();System.out.println("正在睡觉");}}
1.5 拓展
1.5.1 创建对象的流程
Person p = new Person();//短短这行代码发生了很多事情
1.把Person.class文件加载进内存
2.在栈内存中,开辟空间,存放变量p
3.在堆内存中,开辟空间,存放Person对象
4.对成员变量进行默认的初始化
5.对成员变量进行显示初始化
6.执行构造方法(如果有构造代码块,就先执行构造代码块再执行构造方法)
7.堆内存完成
8.把堆内存的地址值赋值给变量p ,p就是一个引用变量,引用了Person对象的地址值
1.5.2 匿名对象
没有名字的对象,是对象的简化表示形式。
使用场景:当被调用的对象只调用一次时。
优点:节省内存,效率高。因为它调用一次后立刻释放内存
缺点:局限性,只能用一次它的属性。
Demo d = new Demo();d.sleep();d.game();//这个d就是对象的名字。也可以写成:new Demo().show();//创建了一个对象调方法new Demo().game();//又创建了一个对象调方法
1.5.3 声明对象类型的数组
package com.atguigu.exer;/** 对象数组题目:定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。问题一:打印出3年级(state值为3)的学生信息。问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息提示:1) 生成随机数:Math.random(),返回值类型double; 2) 四舍五入取整:Math.round(double d),返回值类型long。* * * * */public class StudentTest {public static void main(String[] args) {//Student s1 = new Student();//Student s1 = new Student();//Student s1 = new Student();//Student s1 = new Student();//Student s1 = new Student();//Student s1 = new Student();//声明Student类型的数组Student[] stus = new Student[20]; //String[] arr = new String[10];for(int i = 0;i < stus.length;i++){//给数组元素赋值stus[i] = new Student();//给Student对象的属性赋值stus[i].number = (i + 1);//年级:[1,6]stus[i].state = (int)(Math.random() * (6 - 1 + 1) + 1);//成绩:[0,100]stus[i].score = (int)(Math.random() * (100 - 0 + 1));}//遍历学生数组for(int i = 0;i <stus.length;i++){//System.out.println(stus[i].number + "," + stus[i].state //+ "," + stus[i].score);System.out.println(stus[i].info());}System.out.println("********************");//问题一:打印出3年级(state值为3)的学生信息。for(int i = 0;i <stus.length;i++){if(stus[i].state == 3){System.out.println(stus[i].info());}}System.out.println("********************");//问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息for(int i = 0;i < stus.length - 1;i++){for(int j = 0;j < stus.length - 1 - i;j++){if(stus[j].score > stus[j + 1].score){//如果需要换序,交换的是数组的元素:Student对象!!!Student temp = stus[j];stus[j] = stus[j + 1];stus[j + 1] = temp;}}}//遍历学生数组for(int i = 0;i <stus.length;i++){System.out.println(stus[i].info());}}}class Student{int number;//学号int state;//年级int score;//成绩//显示学生信息的方法public String info(){return "学号:" + number + ",年级:" + state + ",成绩:" + score;}}
优化写法:封装成数组工具类
package com.atguigu.exer;/** 4. 对象数组题目:定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。问题一:打印出3年级(state值为3)的学生信息。问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息提示:1) 生成随机数:Math.random(),返回值类型double; 2) 四舍五入取整:Math.round(double d),返回值类型long。* * * 此代码是对StudentTest.java的改进:将操作数组的功能封装到方法中。* */public class StudentTest1 {public static void main(String[] args) {//声明Student类型的数组Student1[] stus = new Student1[20]; for(int i = 0;i < stus.length;i++){//给数组元素赋值stus[i] = new Student1();//给Student对象的属性赋值stus[i].number = (i + 1);//年级:[1,6]stus[i].state = (int)(Math.random() * (6 - 1 + 1) + 1);//成绩:[0,100]stus[i].score = (int)(Math.random() * (100 - 0 + 1));}StudentTest1 test = new StudentTest1();//遍历学生数组test.print(stus);System.out.println("********************");//问题一:打印出3年级(state值为3)的学生信息。test.searchState(stus, 3);System.out.println("********************");//问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息test.sort(stus);//遍历学生数组test.print(stus);}/*** * @Description 遍历Student1[]数组的操作* @author shkstart* @date 1月15日下午5:10:19* @param stus*/public void print(Student1[] stus){for(int i = 0;i <stus.length;i++){System.out.println(stus[i].info());}}/*** * @Description 查找Stduent数组中指定年级的学生信息* @author shkstart* @date 1月15日下午5:08:08* @param stus 要查找的数组* @param state 要找的年级*/public void searchState(Student1[] stus,int state){for(int i = 0;i <stus.length;i++){if(stus[i].state == state){System.out.println(stus[i].info());}}}/*** * @Description 给Student1数组排序* @author shkstart* @date 1月15日下午5:09:46* @param stus*/public void sort(Student1[] stus){for(int i = 0;i < stus.length - 1;i++){for(int j = 0;j < stus.length - 1 - i;j++){if(stus[j].score > stus[j + 1].score){//如果需要换序,交换的是数组的元素:Student对象!!!Student1 temp = stus[j];stus[j] = stus[j + 1];stus[j + 1] = temp;}}}}}class Student1{int number;//学号int state;//年级int score;//成绩//显示学生信息的方法public String info(){return "学号:" + number + ",年级:" + state + ",成绩:" + score;}}
2 Day07–面向对象2
2.1 构造方法(构造器,构建器,构造函数,constructor)
2.1.1 概念
构造方法是一种特殊的方法,它是一个与类同名且没有返回值的位置(连void都没有)。普通方法也可以与类名相同,但一般不这么写。对象的创建就是通过构造方法来完成,其功能主要是:1.完成对象的创建 。2.可以进行对象属性的初始化。当类实例化一个对象时会自动调用构造方法。构造方法和其他方法一样也可以重载,但不能重写。(因为:1.构造方法不能被继承:如果继承则与子类类名相同。2.是因为子类中有super自动调用父类的构造方法)若自己没有定义构造方法,则默认提供了一个无参构造方法(所以任何一个类都有构造方法) 若自己定义了构造方法,则不再默认提供,所以建议手动写一个无参构造。构造方法的作用:为了方便外界创建对象。idea生成构造方法快捷键:alt+insert.
2.1.2 形式
修饰符 类名([参数列表]){//可以无参也可以有参代码……//没有返回值}
2.1.3 练习1:构造方法创建对象
package cn.tedu.constructor;//测试构造方法的使用public class Test5_Constructor {public static void main(String[] args) {//创建Teacher测试//1,当创建对象时,会自动调用构造方法???--会自动调用,调用了默认就会存在的无参构造Teacher t = new Teacher();//3.1,创建对象时,触发含参构造,根据不同的参数类型来自动调用构造方法.Teacher t1 = new Teacher("tony");}}//创建Teacher类class Teacher{//2,提供构造方法:修饰符 类名([参数列表]){}//4,无参构造 -- 默认就会存在 -- 前提是:没有含参构造时,才存在。如果只提供了含参构造,无参构造就真没了。public Teacher() {System.out.println("无参构造...");}//3,构造方法是一个特殊的方法,特殊在没有返回值,方法名=类名。但是可以存在方法的重载现象//准备重载的构造方法public Teacher(String n) {System.out.println("含参构造..."+n);}}
2.1.4 练习2:构造方法初始化赋值
package cn.tedu.constructor;//这个类用来测试构造方法赋值public class Test6_Constructor2 {public static void main(String[] args) {//自动触发无参构造Student2 s = new Student2();//触发含参构造,在创建对象时给成员变量赋值。Student2 s2 = new Student2(2);}}//创建Student2类class Student2{//成员变量int age;//提供构造方法public Student2() {System.out.println("无参构造");}//提供重载的构造方法public Student2(int a) {//a在方法里面是局部变量//1,构造方法可以用来给变量赋值//流程:当含参的方式创建对象时,会自动触发含参构造。把参数2给a赋值,a拿到值之后再给成员变量age赋值age = a;System.out.println("成员变量age:"+age);System.out.println("含参构造");}}
2.1.5 总结:属性赋值的先后顺序
package .ca;/** 总结:属性赋值的先后顺序* * * ① 默认初始化(初始化:第一次赋值)* ② 显式初始化* ③ 构造器中初始化* * ④ 通过"对象.方法"(加了private封装控制权限了) 或 "对象.属性"的方式,赋值* * 以上操作的先后顺序:① - ② - ③ - ④ * */public class UserTest {public static void main(String[] args) {User u = new User();System.out.println(u.age);User u1 = new User(2);u1.setAge(3);u1.setAge(5);System.out.println(u1.age);}}class User{String name;//默认初始化int age = 1;//显示初始化public User(){}public User(int a){//创建对象,属性初始化age = a;}public void setAge(int a){age = a;}}
2.2 JavaBean可重用组件
package com.atguigu.java1;/** JavaBean是一种Java语言写成的可重用组件。所谓JavaBean,是指符合如下标准的Java类:>类是公共的>有一个无参的公共的构造器>有属性,且有对应的get、set方法* */public class Customer {private int id;private String name;public Customer(){}public void setId(int i){id = i;}public int getId(){return id;}public void setName(String n){name = n;}public String getName(){return name;}}
2.3 构造代码块和局部代码块
2.3.1 构造代码块
1、在类的内部,方法外部,的代码块。
2、通常用于抽取构造方法中的共性代码。可以在创建对象时,对对象的属性等进行初始化。
3、每次调用构造方法前都会调用构造代码块
4、优先于构造方法加载
5、每次创建对象都会先调用一次构造代码块,在调用构造方法
6、内部可以有输出语句
7、如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
8、非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
9、{ }
测试构造代码块:
package cn.tedu.block;//1,创建对象时,会自动触发构造方法。如果有构造代码块会先执行代码块再执行构造方法88888888888888888888888888public class Test1_Block {public static void main(String[] args) {//TODO 创建Person对象测试new Person();//2,每次创建对象都会调用构造代码块和构造方法new Person();new Person("name");new Person(100);}}//创建Person类class Person{String country;//为了让每个构造方法都使用,提高作用范围//b,提供构造代码块 : 位置:在类里方法外{//3,构造代码块:用来提取构造方法的共性country = "中国人";}//a,提供构造方法public Person() {System.out.println("无参构造...,我的国籍是:"+country);}//c,提供重载构造方法public Person(String c) {System.out.println("含参构造...,我的国籍是:"+country);}public Person(int a) {System.out.println("含参构造...,我的国籍是:"+country);}}
2.3.2 局部代码块
1、在方法里面(普通方法和构造方法都可以)的代码块
2、通常用于控制变量的作用范围,出了括号就失效
3、变量的范围越小越好,成员变量会有线程安全问题
4、方法调用时触发
5、方法调用时调用局部代码块。
6. { }
2.3.3 测试:构造,局部代码块加载顺序
构造代码块->构造方法->局部代码块
package cn.tedu.block;//测试代码块//1,创建对象时,会自动触发构造方法。如果有构造代码块会先执行代码块再执行构造方法,//若局部代码块写在构造方法里面,构造方法和局部代码块的顺序是看程序写代码的顺序执行。//若写在普通方法里面,则是调用的时候用(它则是最后用,因为是先创建对象,在通过引用调用方法)。//总结://1,构造代码块:创建对象时触发,在类里方法外,用来抽取构造方法的共性//2,局部代码块:方法调用时触发,在方法里,用来控制变量的作用范围//若没有创建对象,构造代码块都不会自动执行//2.每次创建对象都会调用构造代码块和构造方法public class Test1_Block {public static void main(String[] args) {//TODO 创建Person对象测试new Person();//2,每次创建对象都会调用构造代码块和构造方法new Person();new Person("name");new Person(100).sleep();//触发局部代码块}}//创建Person类class Person{String country;//为了让每个构造方法都使用,提高作用范围//b,提供构造代码块 : 位置:在类里方法外{//3,构造代码块:用来提取构造方法的共性country = "中国人";}//a,提供构造方法public Person() {System.out.println("无参构造...,我的国籍是:"+country);}//c,提供重载构造方法public Person(String c) {System.out.println("含参构造...,我的国籍是:"+country);}public Person(int a) {System.out.println("含参构造...,我的国籍是:"+country);}//提供普通方法public void sleep() {//局部代码块: 位置:在方法里 + 作用:控制变量的作用范围{int i = 10;System.out.println("局部代码块..."+i);}//System.out.println(i);}}
2.4 this关键字
2.4.1 概念
说明:
1.this代表本类对象的一个引用对象。
2.this只用在2个地方:一个是,成员变量名和局部变量名相同时,用this指代成员变量用来区分成员变量和局部变量的区别。另一个是,用来构造方法间进行调用另一个构造方法在第一行使用。
注意:
1.当成员变量和局部变量名不同时可以省略this。
2.在构造函数中,this()必须放在第一行。
2.4.2 形式
name=name;age=age;//解释:其实是想把Student类的局部变量name的值赋值给成员变量,相当于你想操作是这样的:Student.name=name;//但是你不能直接写类名,这时候就用代表本类的对象this来完成。代码变成了:this.name=name;
语法:
1)this.成员变量名-----------访问成员变量(掌握)this.name=name;2)this.方法名()------------调用方法(不用掌握)因为在类中没有相同的方法,所以不用写this来区分。3)this()------------------调用构造方法(只能重载类名相同)根据它的参数列表(一个构造方法可以通过this关键字调用另外一个重载的构造方法)
2.4.3 练习1:当变量名相同时
当局部变量和成员变量同名时,用于区分。
如果附近有同名变量,会遵从变量的就近原则,那么怎么调用成员变量呢?
package cn.tedu.thisdemo;//测试this关键字public class Test2_This {public static void main(String[] args) {//创建Student对象测试Student s = new Student();s.show();}}代码体现1://创建Student类class Student{int count ;//成员变量int sum = 20;//成员变量public void show() {int sum = 10;//局部变量System.out.println(sum);//10,就近原则System.out.println(count);//0//System.out.println(new Student().sum);//20//1,this关键字代表的是本类对象的一个引用就相当于Student this = new Student();8888888888888888888//2,当成员变量和局部变量同名时(如:sum),可以使用this来区分。this调用的是本类的成员变量。888888888888888888888888(当成员变量和局部变量名不同时可以省略this)//(因为局部变量在方法里,想要调用先调用方法才行,所以它调用的变量是成员变量)System.out.println(this.sum);//20}}代码体现2:class Student2{String name;//创建对象时,给成员变量name赋值public Student2(String name) {//name = name;//没有成功的给成员变量name赋值,因为等号左右两边都是局部变量 //就近原则this.name = name;//等号左边使用的是成员变量,右边是局部变量//注意:变量只要在方法上就是局部变量,而不是在{ }里才是。System.out.println(name);System.out.println(this.name);}}
2.4.4 练习2:构造方法间的调用
package cn.tedu.thisdemo;//this在构造方法间调用//总结:this只用在2个地方:一个是,成员变量名和局部变量名相同时,用this指代成员变量用来区分成员变量和局部变量的区别。另一个是,用来构造方法间进行调用另一个构造方法在第一行时用//1,this可以在构造方法间互相调用//2,如果在构造方法里出现了this关键字,必须放在第一条语句的位置//构造方法之间不能同时相互调用,否则会出现死循环,报错。也不能自己调自己。public class Test3_This2 {public static void main(String[] args) {//无参创建对象测试Teacher t = new Teacher();//含参创建对象测试Teacher t2 = new Teacher("jack");}}//创建Teacher类class Teacher{//提供构造方法public Teacher() {//在 无参构造 中访问 含参构造// this("jack");System.out.println("无参构造");}public Teacher(String name) {//在 含参构造 中访问 无参构造this(); System.out.println("含参构造"+name);}}
2.4.5 练习3:综合练习
package com.atguigu.java2;/** this关键字的使用:* 1.this可以用来修饰、调用:属性、方法、构造器* * 2.this修饰属性和方法:* this理解为:当前对象 或 当前正在创建的对象* * 2.1 在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,* 通常情况下,我们都选择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式* 的使用"this.变量"的方式,表明此变量是属性,而非形参。* * 2.2 在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。* 但是,通常情况下,我们都选择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式* 的使用"this.变量"的方式,表明此变量是属性,而非形参。* * 3. this调用构造器* ① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器* ② 构造器中不能通过"this(形参列表)"方式调用自己* ③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)"* ④ 规定:"this(形参列表)"必须声明在当前构造器的首行* ⑤ 构造器内部,最多只能声明一个"this(形参列表)",用来调用其他的构造器* * */public class PersonTest {public static void main(String[] args) {Person p1 = new Person();p1.setAge(1);System.out.println(p1.getAge());p1.eat();System.out.println();Person p2 = new Person("Jerry",20);System.out.println(p2.getAge());}}class Person{private String name;private int age;public Person(){//this.eat();String info = "Person初始化时,需要考虑如下的1,2,3,4...(共40行代码)";System.out.println(info);}public Person(String name){this();this.name = name;}public Person(int age){this();this.age = age;}public Person(String name,int age){this(age);this.name = name;//this.age = age;//Person初始化时,需要考虑如下的1,2,3,4...(共40行代码)}public void setName(String name){this.name = name;}public String getName(){return this.name;}public void setAge(int age){this.age = age;}public int getAge(){return this.age;}public void eat(){System.out.println("人吃饭");this.study();}public void study(){System.out.println("人学习");}}
2.5 Package和import关键字使用
package com.atguigu.java2;import java.lang.reflect.Field;import java.util.*;import com.atguigu.exer4.Account;import com.atguigu.exer4.Bank;import com.atguigu.java2.java3.Dog;import static java.lang.System.*;import static java.lang.Math.*;/** 一、package关键字的使用* 1.为了更好的实现项目中类的管理,提供包的概念* 2.使用package声明类或接口所属的包,声明在源文件的首行(这里的首行要求不严格:指* 的是声明的第一条语句,在它之前有换行的空格也算是首行)* 3.包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”* 4.每"."一次,就代表一层文件目录。* * 补充:同一个包下,不能命名同名的接口、类。*不同的包下,可以命名同名的接口、类。* * 二、import关键字的使用* import:导入* 1. 在源文件中显式的使用import结构导入指定包下的类、接口* 2. 声明在包的声明和类的声明之间* 3. 如果需要导入多个结构,则并列写出即可* 4. 可以使用"xxx.*"的方式,表示可以导入xxx包下的所有结构* 5. 如果使用的类或接口是java.lang包下定义的,则可以省略import结构* 6. 如果使用的类或接口是本包下定义的,则可以省略import结构* 7. 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示。* 8. 使用"xxx.*"方式表明可以调用xxx包下的所有结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入* * 9. import static:导入指定类或接口中的静态结构:属性或方法。 */public class PackageImportTest {public static void main(String[] args) {String info = Arrays.toString(new int[]{1,2,3});Bank bank = new Bank();ArrayList list = new ArrayList();HashMap map = new HashMap();Scanner s = null;System.out.println("hello!");Person p = new Person();Account acct = new Account(1000);//全类名的方式显示com.atguigu.exer3.Account acct1 = new com.atguigu.exer3.Account(1000,2000,0.0123);Date date = new Date();java.sql.Date date1 = new java.sql.Date(5243523532535L);Dog dog = new Dog();Field field = null;out.println("hello");long num = round(123.434);}}
2.6 继承
2.6.1 概念
继承是面向对象最显著的一个特性。
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力.(类于类之间)
Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类/超类/基类。(子类又叫做派生类)
优点:这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。
例:
// A: 子类 C:父类class A extends c{//原来的eat()拿走了 }//注意:继承的语法,第二个类不需要再使用classclass B extends c{//原来的eat()拿走了 }class c{public void eat(){syso("eat");}}
2.6.2 特点
使用extends关键字相当于子类把父类的功能复制了一份(包括变量和方法,私有资源的也可以被继承只不过因为封装性的影响子类不能直接调用,通过创建对象也不能改变,只能通过在公共的方法间接调用私有的资源。)一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只有因为封装性的影响,使得子类不能直接调用父类私有的结构而已。java只支持单继承(一个超类可以有多个派生类,一个派生类只能继承于一个超类)继承可以传递(爷爷,儿子,孙子的关系)子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法不能继承父类的私有成员继承多用于功能的修改,子类可以拥有父类的功能的同时,进行功能拓展是is a 的关系java规定:在构造派生类之前必须先构造超类(先有父亲在有儿子)构造方法不能被继承。2.6.3 Object类理解
如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类意味着,所有的java类具有java.lang.Object类声明的功能。因为Object类属于lang包下所以不用导包。2.6.4 入门案例
package cn.tedu.extendsdemo;//测试继承的入门案例public class Test4_Extends {public static void main(String[] args) {//创建父类对象测试,提高代码的复用性/高内聚,父类写子类的共性Father f = new Father();f.eat();// System.out.println(f.sum);//创建子类对象测试Son s = new Son();//5,子类可以使用父类的所有功能,除了private的s.eat();// System.out.println(s.sum);//6,继承具有传递性,爷爷的功能,孙子类里也能用s.game();System.out.println(s.count);}}class Yeye{//没有指明默认继承Lang包下的Object类int count ;public void game() {System.out.println("下象棋");}}//创建父类//!!耦合性 --继承就是一种强耦合性!!!程序中耦合性越低越好,降低程序的耦合性。 888888888888888888888888(后续用接口)class Father extends Yeye{//4,如果父类中,资源被private修饰,这个资源子类无法继承private int sum =10;public void eat() {System.out.println("爸爸在吃猪肉");}}//创建子类//1,用extends关键字表示继承关系//3,Java只支持单继承,一个子类只能继承于一个父类class Son extends Father{//2,继承后,子类就能使用父类的功能,就相当于子类把父类的功能复制了一份} //继承不会改变源码,而是拓展
2.6.5 测试:私有的属性 方法也能被继承
package .extend;public class Test {public static void main(String[] args) {Student stu = new Student();stu.setAge(15);//吃饭stu.eat();//睡觉/* 说明:此时使用的对象是子类student,通过公共的方法调用私有的资源,* 而私有资源定义在父类person中,那么此时在子类中必定继承了私有资源* 只不过这些私有的资源由于封装性无法直接使用* */}}class Person {String name;private int age;public void eat(){System.out.println("吃饭");sleep();}private void sleep(){System.out.println("睡觉");}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}class Student extends Person{private String name = "小明";}
2.6.6 成员变量的使用
package cn.tedu.extendsdemo; //测试继承中成员变量的用法//可以使用从父类中继承过来的,也可以用自己特有的public class Test5_UseExtends {public static void main(String[] args) {//TODO 创建子类对象测试Erzi zi = new Erzi();System.out.println(zi.count);//20System.out.println(zi.sum);}}//创建父类class BaBa{int count = 10;}//创建子类class Erzi extends BaBa{int count = 20;int sum = 20;}
2.6.7 成员方法的使用
package cn.tedu.extendsdemo;//测试继承中成员方法的用法public class Test5_UseExtends {public static void main(String[] args) {//TODO 创建子类对象测试Erzi zi = new Erzi();//可以使用父类的功能,也可以使用自己特有的功能System.out.println(zi.count);//20System.out.println(zi.sum);//2,如果子类没有发生方法重写,eat()使用父类的功能。//如果子类发生了方法重写,使用的就是重写之后,也就是子类的eat()的功能。888888888888zi.eat();//爸爸在吃肉 --》儿子在喝汤zi.code();//可以使用特有的方法}}//创建父类class BaBa{int count = 10;public void eat() {System.out.println("爸爸在吃肉");}}//创建子类class Erzi extends BaBa{int count = 20;int sum = 20;//1,方法重写:出现的原因是:当父类的功能需要修改时,我们不能直接打开父类代码修改源码!!我可只能功能扩展//扩展的过程:先发生继承关系,然后发生方法重写的现象。要求子类的方法声明和父类一模一样。public void eat() {System.out.println(count);//20System.out.println(super.count);//10,通过super调用父类的功能System.out.println("儿子在喝汤");}//提供子类特有方法public void code() {System.out.println("儿子在敲代码");}}
2.6.8 构造方法的使用(构造方法不能被继承)
1、子类创建对象时,默认会去访问父类的无参构造方法
2、在构造方法的第一行,都有一条默认的语句:super();
3、父类没有无参构造时,可以用super调用父类的其他构造
package cn.tedu.extendsdemo;//测试继承中构造方法的使用public class Test6_UseExtends2 {public static void main(String[] args) {//TODO 创建子类对象测试Zi zi = new Zi();}}//创建父类class Fu{//如果父类中,只提供含参构造,这时,无参构造就会被覆盖,没了!//建议提供一个无参构造。public Fu(String n) {System.out.println("Fu()...");}}//创建子类class Zi extends Fu{public Zi() {//0,super关键字常用于子类中,当子类要使用父类功能时,通过super代表父类对象的引用。//1,子类创建对象时,默认就会存在super(),也就默认就会去访问父类的无参构造。// super();//父类没有无参构造了!!//2,当父类中不提供无参构造时,只能通过super调用父类的含参构造//3,super关键字如果在构造方法中使用,必须是第一条语句。super("tony");System.out.println("Zi()...");}}
2.7 super关键字
说明:
1、通过super关键字可以使用父类的内容
2、super代表父类的一个引用对象
3、如果在构造方法中使用,必须是第一条语句
语法:
1)super.成员变量名------访问超类的成员变量(用于:子父类定义同名的变量,想要在子类中使用父类的变量时使用。父子类定义2个相同名字的变量不会覆盖,变量没有覆盖功能。)2)super.方法名()-------调用超类的方法(用于:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。)3)super()--------------调用超类的构造方法
问题:为什么在实例化子类的对象的时候会调用先调用父类的构造函数?
答:因为子类继承父类之后,获取到了父类的内容(属性/字段),而这些内容在使用之前必须先初始化,所以必须先调用父类的构造函数进行内容的初始化.
注意:
super只能出现在子类的方法或者构造方法中。在子类构造方法只能有一个super。super当在子类的构造方法中调用父类的构造方法,则必须位于子类构造方法的第一行。(在普通方法无这一要求)即使不写默认处于子类构造方法中第一行,来调用父类的无参构造。如果父类重载类构造方法,它的子类super需要根据传参的不同进行调用。在子类的构造方法中,可以调用父类的构造方法,也可以调用父类的普通方法。在子类的普通方法只能调用父类普通方法,不能调用构造方法。
2.7.1 子类对象实例化的全过程
子类对象实例化的全过程:
从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调其父类的构造器,进而调用父类的父类的构造器,…
直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
2.8 this与super的区别
代表的对象不同:
this:代表本类对象的引用。super:代表父类对象的引用。
前提:
3. this: 没有继承也可以使用。
4. super: 只有在继承条件才可以使用。
调用构造方法时:
5. this: 本类的构造
6. super: 父类的构造
注意事项:this和super不可以同时出现在同一个构造方法里,他们两个只要出现都得放在构造方法第一行,同时出现的话,到底第一行放谁呢。
2.9 方法的重写
前提:发生在父子类中。
遵循两同两小一大原则:
两同:方法名相同,参数列表相同(注意:参数列表的相同不相同只和形参类型有关,和形参名字没有关系。)两小:1: 子类的返回值类型小于或等于父类的1.1void和基本类型时,必须相同1.2引用类型时:小于或等于父类的2:子类抛出的异常小于或等于父类的一大:子类的访问权限大于或等于父类的
注意:
重写指的是方法的重写,属性没有重写功能(重载也是)。重写方法体也可以相同,类似于重载只不过没啥意义,一般都是不同。父类中的私有方法不能被重写,因为私有的不能被继承。static方法不能被重写,属于类,它不属于实例。子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。final修饰的方法不能被重写。构造方法不能被重写。重写方法的标识为@Override注解:有特定的小功能,标志了这个方法重写了父类的功能。
为什么要进行重写???
当父类的功能,子类不需要或不一定满足时就需要重写。
2.9.1 重写入门案例
package cn.tedu.extendsdemo;//测试继承中成员方法的用法public class Test5_UseExtends {public static void main(String[] args) {//TODO 创建子类对象测试Erzi zi = new Erzi();//可以使用父类的功能,也可以使用自己特有的功能System.out.println(zi.count);//20System.out.println(zi.sum);//2,如果子类没有发生方法重写,eat()使用父类的功能。//如果子类发生了方法重写,使用的就是重写之后,也就是子类的eat()的功能。888888888888zi.eat();//爸爸在吃肉 --》儿子在喝汤zi.code();//可以使用特有的方法}}//创建父类class BaBa{int count = 10;public void eat() {System.out.println("爸爸在吃肉");}}//创建子类class Erzi extends BaBa{int count = 20;int sum = 20;//1,方法重写:出现的原因是:当父类的功能需要修改时,我们不能直接打开父类代码修改源码!!我可只能功能扩展//扩展的过程:先发生继承关系,然后发生方法重写的现象。要求子类的方法声明和父类一模一样。public void eat() {System.out.println(count);//20System.out.println(super.count);//10,通过super调用父类的功能System.out.println("儿子在喝汤");}//提供子类特有方法public void code() {System.out.println("儿子在敲代码");}}
2.10 拓展
2.10.1 重写与重载的区别(Overload和Override的区别)
1、重载:是指同一个类中的多个方法具有相同的名字,但这些方法具有不同的参数列表,即参数的数量或参数类型不能完全相同
2、重写:是存在子父类之间的,子类定义的方法与父类中的方法具有相同的方法名字,相同的参数表和相同的返回类型
3、重写是父类与子类之间多态性的一种表现
4、重载是一类中多态性的一种表现
2.10.2 继承的内存结构
3 Day08–面向对象3
3.1 static
3.1.1 概念
1、是java中的一个关键字
2、用于修饰成员(成员变量和成员方法,静态代码块),不能修饰局部资源,不能修饰构造方法。
3.1.2 特点
1、可以修饰成员变量,成员方法,静态代码块。不可以修饰类,静态内部类除外。
2、随着类的加载而加载,优先于对象加载
3、只加载一次,就会一直存在,不再开辟新空间
4、全局唯一,全局共享
5、可以直接被类名调用(静态资源都可以通过类名点调用)
6、静态方法不能被重写。
注意:静态属性、静态方法和私有的属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态。静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成调用,不需要继承即可调用 。
问题:静态只能调用静态,非静态可以调用静态和非静态,为什么?
因为:静态资源随着类的加载而加载,优先于对象加载,类加载完成时对象还没有创建,所以不能调用。
同理static不能和this或者super共用:this,super代替的是对象,有static时可能还没有对象 ,main方法也是静态方法所以不写this,super。
调用关系总结:
同一个类之间的调用不用创建对象,不同类之间资源调用通过创建对象或是用静态资源。
非静态资源(实例变量和普通方法,不包括局部变量,因为局部变量想要调用都要先调用方法)只能通过创建对象来使用,静态资源(静态变量和静态方法)可以通过创建对象也可以通过类名.直接调用(静态资源推荐使用类名调用)。
3.1.3 入门案例
package cn.tedu.staticdemo;//static一般放在返回值类型之前。 //这个类用来测试静态入门案例public class Test1_Static {public static void main(String[] args) {//2,静态资源优先于对象加载,会优先加载进内存//静态资源访问:有2种。//1:通过new对象进行访问·(但是不推荐用)//2:直接通过类名.访问//非静态资源只能有一种:通过对象Person.game();System.out.println(Person.age);//TODO 创建Person对象测试Person p = new Person();p.eat();System.out.println(p.name);//1.1, 静态资源,可以通过对象访问p.game();System.out.println(p.age);//1, 静态资源,还可以通过类名访问Person.game();System.out.println(Person.age);//3,静态资源,在多个对象间,是共享的Person p1 = new Person();Person p2 = new Person();p1.age=10;System.out.println(p2.age);//10}}//创建Person类class Person{//普通资源String name;public void eat() {System.out.println("eat()...");}//静态资源 -- 使用static修饰static int age;static public void game () {System.out.println("game()...");}}
3.1.4 综合案例
package com.atguigu.java1;/** static关键字的使用* * 1.static:静态的* 2.static可以用来修饰:属性、方法、代码块、内部类* * 3.使用static修饰属性:静态变量(或类变量)* 3.1 属性,按是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)* 实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的* 非静态属性时,不会导致其他对象中同样的属性值的修改。* 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致* 其他对象调用此静态变量时,是修改过了的。* 3.2 static修饰属性的其他说明:* ① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用*② 静态变量的加载要早于对象的创建。*③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。**④类变量实例变量*类yesno*对象yesyes**3.3 静态属性举例:System.out; Math.PI;* * 4.使用static修饰方法:静态方法* ① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用* ②静态方法非静态方法*类yesno*对象yesyes* ③ 静态方法中,只能调用静态的方法或属性* 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性* * 5. static注意点:* 5.1 在静态的方法内,不能使用this关键字、super关键字* 5.2 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。* * 6. 开发中,如何确定一个属性是否要声明为static的?* > 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。* > 类中的常量也常常声明为static* * 开发中,如何确定一个方法是否要声明为static的?* > 操作静态属性的方法,通常设置为static的*> 工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections*/public class StaticTest {public static void main(String[] args) {Chinese.nation = "中国";Chinese c1 = new Chinese();c1.name = "姚明";c1.age = 40;c1.nation = "CHN";Chinese c2 = new Chinese();c2.name = "马龙";c2.age = 30;c2.nation = "CHINA";System.out.println(c1.nation);//编译不通过//Chinese.name = "张继科";c1.eat();Chinese.show();//编译不通过//Chinese.eat();//Chinese.info();}}//中国人class Chinese{String name;int age;static String nation;public void eat(){System.out.println("中国人吃中餐");//调用非静态结构this.info();System.out.println("name :" +name);//调用静态结构walk();System.out.println("nation : " + nation);}public static void show(){System.out.println("我是一个中国人!");//不能调用非静态的结构//eat();//name = "Tom";//可以调用静态的结构System.out.println(Chinese.nation);walk();}public void info(){System.out.println("name :" + name +",age : " + age);}public static void walk(){}}
3.1.5 静态方法内存图
内存:由JVM管理的
1.堆:所有new出来的对象(包括 new出来的对象和成员变量)
2.栈:局部变量(包括方法的参数 基本类型的数值 引用类型的地址)
3.方法区:.class字节码文件,静态方法区,常量池。
3.2 静态代码块
3.2.1 概述
1、静态代码块:在类加载时就加载,并且只被加载(调用)一次,一般用于项目的初始化 。
2、位置:在类里方法外
3.2.2 特点
内部可以有输出语句随着类的加载而执行,而且只执行一次作用:初始化类的信息如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行静态代码块的执行要优先于非静态代码块的执行静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构语法:
static{…}
3.2.3 测试静态,构造,局部代码块加载顺序
执行顺序是:静态代码块最快,因为它是类加载的时候被自动调用,且只调用一次其次是构造代码块,构造方法,局部代码快。
package cn.tedu.block;//测试代码块//总结://1, 触发时间节点:调用方法时,会先进入类里面,然后会自动调用静态块(即,类被第一次加载时)//2,位置 :类里面,方法外//3,作用/功能:只被加载一次的时候用。//执行顺序是:静态代码块最快,因为它是类加载的时候被自动调用,且只调用一次其次是构造代码块,构造方法,局部代码快。public class Test3_Block {public static void main(String[] args) {//TODO 创建对象测试TestBlock tb = new TestBlock();tb.show();//触发局部代码块TestBlock tb2 = new TestBlock();}}//创建TestBlock类class TestBlock{//1,提供构造代码块{System.out.println("构造代码块");}//2,提供静态代码块:加载的早,而且只加载一次static{System.out.println("静态代码块");}//3,提供构造方法public TestBlock() {System.out.println("构造方法");}//4,提供局部代码块public void show() {{System.out.println("局部代码块");}}}
3.2.4 总结: 静态代码块,构造代码块,局部代码块
静态代码块static{ }:在类加载时就加载,并且只被加载一次,一般用于项目的初始化,类里方法外。
构造代码块{ }:在创建对象时会自动调用,每次创建对象都会被调用,通常用于抽取构造方法中的共性代码,可以在创建对象时,对对象的属性等进行初始化类里方法外。
局部代码块{ }:调用方法时调用,用于控制变量的作用范围,方法中。
3.2.5 拓展: 静态代码块和静态方法区别
如果有些代码必须在项目启动的时候执行,需要使用静态代码块,这种代码是主动执行的;如果需要在项目启动的时候初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的。静态方法在类加载的时候加载,可以用类名直接调用。比如:main方法就必须是静态的 ,这是程序入口。两者的区别:静态代码块是自动执行的,静态方法是被调用的时候才执行的。
3.2.6 成员变量赋值最终篇
package com.atguigu.java3;/** 对属性可以赋值的位置:* ①默认初始化* ②显式初始化/⑤在代码块中赋值(② ⑤谁在前谁在后,和定义的顺序有关)* ③构造器中初始化* ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值* * * 执行的先后顺序:① - ② / ⑤ - ③ - ④*/public class OrderTest {public static void main(String[] args) {Order order = new Order();System.out.println(order.orderId);}}class Order{int orderId = 3;{orderId = 4; //最终输出为4}//还可以写成:相当于定义了2个变量顺序无所谓{orderId = 4;}int orderId = 3;//输出为3}
3.3 final
3.3.1 概念
是java提供的一个关键字。final是最终的意思。final可以修饰类,方法,变量,成员 局部都可以。初衷是因为:java出现了继承后,子类可以更改父类的功能,当父类功能不许子类改变时可以利用final关键字修饰父类。
3.3.2 特点
被final修饰的类,不能被继承(好处:使一个类不能被继承的意义在于:可以保护类不被继承修改,可以控制滥用继承对系统造成的危害)。如:String类、System类、StringBuffer类。
被final修饰的方法,不能被重写,但可以重载。 如:Object类中getClass();
被final修饰的变量是个常量,值不能被更改。
final修饰属性(成员变量):可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化(不能通过常见对象进行赋值,因为构造方法执行完后才会出现对象,在堆中已经有默认初始化的值了,此时在赋值相当于修改常量,而常量不能修改)
final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
注意:final修饰引用类型的变量时候是指引用不可改变,至于对象的属性可以随意更改。
常量的定义形式:final 数据类型 常量名 = 值;
,一般为了方便外界调用,都会被static修饰,可以直接被类名.访问。最终形式为全局常量:static final 数据类型 常量名 = 值;
。
常量命名规范:推荐常量名所有字母大写,多个单词用下划线(“_”)隔开(推荐就是不强制)
常量注意事项:
必须声明同时初始化(必须直接赋初值,不能修改),(静态块中赋值也可以,但不推荐)由类名点来访问,并且不能改变
常量优点:常量在编译期被直接替换为具体的值- -效率高
常量何时用?数据永远不变并且经常被使用。
3.3.3 入门案例
package com.atguigu.java3;/** final:最终的* * 1. final可以用来修饰的结构:类、方法、变量* * 2. final 用来修饰一个类:此类不能被其他类所继承。*比如:String类、System类、StringBuffer类* * 3. final 用来修饰方法:表明此方法不可以被重写* 比如:Object类中getClass();* * 4. final 用来修饰变量:此时的"变量"就称为是一个常量*4.1 final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化* 4.2 final修饰局部变量:* 尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值* 以后,就只能在方法体内使用此形参,但不能进行重新赋值。* * static final 用来修饰属性:全局常量*/public class FinalTest {final int WIDTH = 0;final int LEFT;final int RIGHT;//final int DOWN;{LEFT = 1;}public FinalTest(){RIGHT = 2;//多个构造器都要赋值否则报错}public FinalTest(int n){RIGHT = n;}//public void setDown(int down){//this.DOWN = down;//}public void doWidth(){//width = 20;}public void show(){final int NUM = 10;//常量//NUM += 20;}public void show(final int num){//num = 20;//编译不通过System.out.println(num);}public static void main(String[] args) {int num = 10;num = num + 5;FinalTest test = new FinalTest();//test.setDown(3);test.show(10);}}final class FinalA{}//class B extends FinalA{////}//class C extends String{////}class AA{public final void show(){}}class BB extends AA{//public void show(){////}}
3.4 多态
3.4.1 概念
多态指同一个实体同时具有多种形式。它是面向对象程序设计(OOP)的一个重要特征。主要是指同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
对象的多态性:父类引用指向子类对象(或子类的对象赋给父类的引用)。
举例1:
// 可以指向不同的子类对象,体现多态性Animal a = new Dog();//父类 子类Animal a = new Cat();
举例2:
水,在不同时刻可以有多种形态,包括水蒸气,冰,水。
Java怎么体现多态呢?狗有两种形态:狗和小动物
class Animal {public void eat(){}}class Dog extends Animal{}class Test1{main(){//创建子类对象测Dog d = new Dog();//小狗就是小狗//创建多态对象测试Animal a = new Dog();//小狗是小动物(是父类类型就是多态)//父类引用 ,指向 子类对象//编译ctrl+s看左边,运行ctrl+f11看右边}}
3.4.2 特点
1、多态的前提1:2个类有继承关系
2、多态的前提2:要有方法的重写
3、多态指的是方法的多态,属性没有多态(因为属性不存在重写)。
4、属性:调用的是父类的属性。
5、方法:调用了父类的方法声明,输出的是子类的重写父类方法的方法体(静态方法除外)
6、静态方法 :可以被继承和隐藏但不能重写,因此不能实现多态。
7、 父类引用指向子类对象,如:Animal a = new Dog(); - -大类型到小类型,又叫作向上转(造)型
8、pereson p = new Son();能调用什么永远只和等号左边有关系,和右边没关系
9、 多态中,编译看左边(能调用什么看左边),运行看右边(能输出什么看右边)
我们写的代码就是.java源文件, ctrl +s会自动将.java文件编译为.class字节码文件。
3.4.3 入门案例
package cn.tedu.duotai;//测试多态的入门案例public class Test5_Duotai {public static void main(String[] args) {//TODO 创建父类对象测试Animal a = new Animal();a.eat();//就是使用父类自己的功能//TODO 创建子类对象测试Dog d = new Dog();d.eat();//重写前,用父类的。重写后,执行的就是子类的。//TODO 创建多态对象测试Animal an = new Dog();//口诀1:父类引用 指向 子类对象//口诀2:编译看左边,运行看右边//编译看左边:想要能够保存成功,只能使用左边也就是父类提供的方法声明部分//运行看右边:是指发生了重写后,执行结果以子类为准an.eat();//调用了父类的方法声明,输出的是子类的重写父类方法的方法体。 888888888888888//多态主要用来统一调用标准:所有方法的调用向父类看齐}}//1, 多态的前提:继承+重写//创建父类class Animal{public void eat() {System.out.println("爸爸在吃肉");}}//创建子类class Dog extends Animal{@Override //它会检查是否写了方法重写,2者一块出现public void eat() {System.out.println("儿子在喝汤");}//子类特有方法,多态对象不能调用!!!!想用,可以创建子类对象用public void eat2() {System.out.println("儿子在喝汤");}}
3.5 多态的好处
多态可以让我们不用关心某个子类对象到底是什么具体类型统一用父类来接收,就可以使用该子类对象的某些方法。这样可以写出通用的代码,做出通用的编程。(即:统一调用标准,标准就是父类。)提高了程序的扩展性和可维护性3.5.1 测试:多态的好处
package .extend;import java.sql.Connection;//多态性的使用举例一:public class AnimalTest {public static void main(String[] args) {AnimalTest test = new AnimalTest();test.func(new Dog());test.func(new Cat());}/* 情景:想要使用子类特有的方法。* 有多态 :只需要定义一个方法,方法的参数是父类对象类型,调用时传递不同的子类对象来调用不同子类特有方法。 * 没有多态:需要定义多个重载的方法,每个方法定义对应的子类对象类型,调用时传递不同的子类对象来调用不同子类特有方法。*/public void func(Animal animal){//Animal animal = new Dog(); 有多态animal.eat();animal.shout();if(animal instanceof Dog){Dog d = (Dog)animal;d.watchDoor();}}//public void func(Dog dog){ 没有多态每次需要定义重载的方法 Dog dog = new Dog();//dog.eat();//dog.shout();//}//public void func(Cat cat){ Cat cat = new Cat();//cat.eat();//cat.shout();//}}class Animal{public void eat(){System.out.println("动物:进食");}public void shout(){System.out.println("动物:叫");}}class Dog extends Animal{public void eat(){System.out.println("狗吃骨头");}public void shout(){System.out.println("汪!汪!汪!");}public void watchDoor(){System.out.println("看门");}}class Cat extends Animal{public void eat(){System.out.println("猫吃鱼");}public void shout(){System.out.println("喵!喵!喵!");}}//举例二:class Order{public void method(Object obj){//只需要声明一个方法接收它的本身或子类对象类型。}}//举例三:class Driver{/* conn = new MySQlConnection(); conn = new OracleConnection(); * 比如Connection是操作数据库连接对象Api的统一父类对象类型,通过父类的引用调用方法操作数据库* 的步骤是固定的几步都是调用这几个方法。* 现在连接的是mysql只需要调用方法时传递mysql的连接对象,* 变为连接的是oracle数据库的对象,只需要在调用方法时传递oracle的对象即可,* 连方法中的代码都不用改变(因为多态调用的是父类方法的声明,输出的是子类重写父类方法的方法体)。* */public void doData(Connection conn){//规范的步骤去操作数据//conn.method1();//conn.method2();//conn.method3();}}
3.6 类型转换(向上造型/向下造型)
说明:
只能是父类的引用指向之类的对象。向上造型:自动转换向下造型:需要强转(很少用),一般用于多态中使用子类特有的资源java中认为超类大,派生类小。java中的基本类型的强转不会报错,但引用类型转换成功只有2种子类转化为父类可能会丢失一些自己本来的方法。
强制类型转换(向下造型),成功的条件只有如下两种:
引用所指向的对象,就是该类型 (引用指向的对象指的是 向上造型里面的的引用所指的对象,与向下造型括号里面的比较。)引用所指向的对象,实现了该接口或继承了该类
建议: 强转时若不满足如上条件,则发生ClassCastException类型转换异常。通常在强转之前先通过instanceof(结果为true或false)来判断引用指向的对象是否是该类型。
3.6.1 向下造型:instance of
作用:判断这个对象是否是这个特定类或者是它的子类的一个实例。
举例1:
package com.shuai;public class Demo04 {public static void main(String[] args) {//类型之间的转换:类似于基本类型转换 父(高) 子(低)//高 低Person1 person1 = new Student1();//向上造型//person1.go();报错,父类里面没有子类特有的方法,无法调用//解决:将person1转化为Student1类型,就可以使用了Student1 ss = (Student1) person1; //向下造型:想用子类特有的方法。ss.go();//2句话写成一块:((Student1) person1).go(); 需要多写个括号}}class Person1{public void run(){System.out.println("输出run方法");}}class Student1 extends Person1{public void go(){System.out.println("输出go方法");}}
举例2:
package .ins;import java.util.Date;public class PersonTest {public static void main(String[] args) {Person p1 = new Person();p1.eat();//创建父类对象,调用父类的方法Man man = new Man();man.eat(); //创建子类对象,调用子类的方法man.age = 25;man.earnMoney();Person p2 = new Man();p2.eat(); //多态,属性没有多态调用都是父类,方法调用的是父类方法声明以及子类对应的方法体。p2.walk();//不能调用子类所特有的方法、属性:编译时,p2是Person类型。(多态也只是调用重写后的方法,不是特有的方法)p2.name = "Tom";//p2.earnMoney();//p2.isSmoking = true;//有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致//编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。//如何才能调用子类特有的属性和方法?/* 1.向下转型:使用强制类型转换符。 * 2.直接创建子类的对象进行调用*/Man m1 = (Man)p2;m1.earnMoney();m1.isSmoking = true;//使用强转时,可能出现ClassCastException的异常。//Woman w1 = (Woman)p2; //w1.goShopping();/** instanceof关键字的使用* * 引用实例 类型* a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。* * * 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先* 进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。* * 如果 a instanceof A返回true,则 a instanceof B也返回true.* 其中,类B是类A的父类。(a A同一个字母表示是同一个变量类型是他本身的实例,字母B不同为true一定为父类。* 即本身赋值给P2的值就是Man类型)*/if(p2 instanceof Woman){Woman w1 = (Woman)p2;w1.goShopping();System.out.println("******Woman******");}if(p2 instanceof Man){//由Person p2 = new Man();可以看出P2是Man类型类型对象的一个实例Man m2 = (Man)p2;m2.earnMoney();System.out.println("******Man******");}if(p2 instanceof Person){//是它父类的子类实例也可以System.out.println("******Person******");}if(p2 instanceof Object){System.out.println("******Object******");}//if(p2 instanceof String){////}//练习://问题一:编译时通过,运行时不通过//举例一://Person p3 = new Woman();//Man m3 = (Man)p3;//举例二://Person p4 = new Person();//Man m4 = (Man)p4;//问题二:编译通过,运行时也通过//Object obj = new Woman();//Person p = (Person)obj;//问题三:编译不通过//Man m5 = new Woman();//String str = new Date();//Object o = new Date();//String str1 = (String)o;}}//class Order{////}class Person {String name;int age;int id = 1001;public void eat(){System.out.println("人:吃饭");}public void walk(){System.out.println("人:走路");}}class Man extends Person{boolean isSmoking;int id = 1002;public void earnMoney(){System.out.println("男人负责挣钱养家");}public void eat(){System.out.println("男人多吃肉,长肌肉");}public void walk(){System.out.println("男人霸气的走路");}}class Woman extends Person{boolean isBeauty;public void goShopping(){System.out.println("女人喜欢购物");}public void eat(){System.out.println("女人少吃,为了减肥");}public void walk(){System.out.println("女人窈窕的走路");}}
3.7 main方法的用法
3.7.1 案例1:测试main方法的作用
package com.atguigu.java2;/** main()方法的使用说明:* 1. main()方法作为程序的入口* 2. main()方法也是一个普通的静态方法* 3. main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)* * * */public class MainTest {public static void main(String[] args) {//入口Main.main(new String[100]);MainTest test = new MainTest();test.show();}public void show(){}}class Main{public static void main(String[] args) {for(int i = 0;i < args.length;i++){args[i] = "args_" + i;System.out.println(args[i]);}}}
3.7.2 案例2:与控制台的交互方式
在eclipse中:
1.首先运行main方法生成字节码文件
2.选中Run Configurations
3.此时会自动定位到刚才运行的程序
4.添加参数
5.运行结果
在Dos命令窗口:
1.复制.java文件并去掉代码中的包名—>放在磁盘目录中
2.打开命令行窗口
3.输入参数运行
3.8 拓展
3.8.1 静态变量和实例变量的区别
在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了,当然通过对象也可以。
总之,实例变量必只能通过创建对象来使用,静态变量可以通过创建对象也可以通过类名.直接调用(静态变量推荐使用类名调用)。
4 Day09–面向对象4
4.1 访问控制符
用来控制一个类
,或者类中的成员变量
,方法
的访问范围。(不能用于局部变量
)
public:公开的,同一个项目的任意位置,任意类中protected:受保护的,本类、同包类、同包子类 /不通包子类。注意不通包只能在子类中使用,同包只要是同一个包下都能使用。默认的:什么也不写,本类、同包类(同一个包中的类),但是不能把default写出来private:私有的,本类
注意:
像这种修饰符public,protected,default,private,static,final,abstract不区分先后顺序,比如修饰方法只需要在返回值类型即可。类的访问修饰符只能默认的
或者是public
(一个.java文件中public修饰的类只能有一个,且类名与文件名相同)类中成员(变量,方法)的访问修饰符如上4种都可以习惯一般数据(成员变量)私有化(private)行为(方法)公开化(public)) 。如果是继承关系中的父类,里面是权限至少是 protected,因为要使用它的派生类要调用父类资源。
4.2 抽象类
4.2.1 概念
Java中可以定义没有方法体的方法,该方法由其子类来具体的实现。该没有方法体的方法我们称之为抽象方法,含有抽象方法的类我们称之为抽象类。
抽象类可以理解为是一个只有方法声明没有方法体的特殊类。
公式:
//抽象方法,没有方法体,大括号也没有,有一个”;”。修饰符 abstract 返回值类型 方法名(参数列表);
抽象类的意义:可以把代码共性进行抽取,子类继承进行扩展,提高代码开发效率。
举例:水果,东西。。
class A{public void eat(){//声明一样,可以提取syso("eat...B") }}class B{public void eat(){//声明一样,可以提取syso("eat…A") }}abstract class C{public abstract void eat();}
4.2.2 特点
通过java关键字abstract实现。可以修饰方法或者类。含有抽像方法的类一定是抽象类,抽象类不一定含有抽象方法(因为可以由子类去实现)。抽象类中可以没有抽象方法(由子类去实现),可以有抽象方法,也可以有普通方法,必有构造方法(用于子类实例化),可以有成员变量,常量。如果类中有抽象方法,那该类必须定义为一个抽象类。子类继承了抽象类以后,(1)要么还是一个抽象类,(2)要么就把所有
父类的抽象方法都重写。多用于多态中。抽象类不可以被new实例化对象但是可以new数组。抽象类为什么有构造方法:没有的话你的子类将无法编译,因为在任何构造函数中的第一条语句隐式调用super()。
abstract使用上的注意点:
abstract不能用来修饰:属性、构造器等结构。abstract不能用来修饰私有方法、静态方法、final的方法、final的类。
总结:不能同时出现的情况:
final 和abstract不能同时出现因为fianl是固定的不能被继承,而
abstract 一般不单独使用,和派生类一起使用this 和super不能连用,因为在构造方法都在第一行static 和this super不能同时出现,因为有static时还没有创建对象。(main方法也是静态方法所以不能有this,super)
注意:修饰符 final static abstract 几乎都在void之前写
4.2.3 入门案例
package cn.tedu.abstractdemo;//测试抽象类的入门案例 public class Test1_Abstract {public static void main(String[] args) {//TODO 创建多态对象测试//7,抽象类不能被实例化new 所以我们可以用多态实例它普通子类的对象//Animal a = new Dog();Animal a = new Cat();a.eat();//狗吃肉}}//创建父类//3,如果类里有抽象方法,这个必须声明成一个抽象类abstract class Animal{//1, 父类提供的方法,如果子类要改,改的是方法体,但是要求方法声明不许改!!!(及:不用父类的方法体,甚至要覆盖它,可以不写父类的方法体就是抽象方法)//这时,我们能不能只提供方法声明,不提供方法体 -- 可以,此时的方法就没有了方法体,称为抽象方法//2, 通过abstract来修饰成抽象的abstract public void eat() ;//4,再提供一个抽象方法 和 普通方法abstract public void game();public void chiji() {System.out.println("正在吃鸡");}}//创建子类//5,子类继承抽象后,子类可以把所有抽象方法 全都重写,那就是一个普通子类class Cat extends Animal{//重写所有抽象方法// 注解:1,说明发生了方法的重写2.检出这个地方必须会发生重写,只有这个注解不写方法会报错@Override //快捷键 @ alt+ ///也可以直接,重写的方法名+alt+/public void eat() {System.out.println("猫吃鱼");}@Overridepublic void game() {System.out.println("玩毛线");}}//6,子类继承抽象后,没有把所有抽象方法 全都重写,那就是一个抽象子类abstract class Dog extends Animal{//重写抽象方法@Overridepublic void eat() {System.out.println("狗吃肉");}}
4.3 抽象类的用法
4.3.1 构造方法
抽象类也有构造方法,但是不能本身实例化。
那抽象类的构造函数有啥用?
一般用于给子类实例化。(因为子类有super默认调用父类构造方法。)
package cn.tedu.abstractdemo;//测试抽象类构造方法的用法public class Test2_UseAbstract {public static void main(String[] args) {//3,抽象类不能被实例化,那提供构造方法有啥用??--用来创建子类对象。//原因是:子类创建对象时,构造方法里默认就会存在super()//Animal2 a = new Animal2();//TODO 创建多态对象测试Animal2 a = new Dog2();}}//创建父类abstract class Animal2{//1,可以有构造方法 -- 作用:用来创建对象public Animal2() {//最好手动提供一个无参构造System.out.println("Animal2()...");}}//创建子类class Dog2 extends Animal2{public Dog2() {super();//2,默认就存在,会自动调用父类的构造方法System.out.println("Dog2()...");}}
4.3.2 抽象类的成员变量和常量
既可以有变量,也可以有常量。
package cn.tedu.abstractdemo;//测试抽象类的成员变量和常量的用法public class Test2_UseAbstract {public static void main(String[] args) {//3,抽象类不能被实例化,那提供构造方法有啥用??--用来创建子类对象。88888888888888888//原因是:子类创建对象时,构造方法里默认就会存在super()//Animal2 a = new Animal2();//TODO 创建多态对象测试Animal2 a = new Dog2();System.out.println(a.sum);//10System.out.println(a.NAME);//xiongdaSystem.out.println(Animal2.NAME);//xiongda}}//创建父类abstract class Animal2{//a,抽象类里可以提供成员变量int sum = 10;//b,抽象类里可以提供成员常量static final String NAME="xiongda";//1,提供构造方法 -- 作用:用来创建对象public Animal2() {System.out.println("Animal2()...");}}//创建子类class Dog2 extends Animal2{public Dog2() {super();//2,默认就存在,会自动调用父类的构造方法System.out.println("Dog2()...");}}
4.3.3 抽象类的成员方法
抽象类里,既可以有普通方法,有可以有抽象方法。抽象类里可以都是普通方法吗? – 可以抽象类里如果都是普通方法,为什么要被修饰成抽象类呢?—不让外界new(不让外界实例化对象)抽象类里可以有普通方法吗? – 可以抽象类可以实例化吗? – 不可以抽象类是一个特殊的类,特殊在哪儿? – 可以包含抽象方法怎么去决定,给抽象类提供的是普通方法还是抽象方法呢?–看要不要给方法体测试:package cn.tedu.oop;//测试 抽象类 成员方法public class Test1_UseAbstract {public static void main(String[] args) {//创建多态对象测试Fu fu = new Zi();fu.eat();fu.sleep();fu.game();}}//2, 如果类中包含抽象方法,那么,这个类必须修饰成 抽象类//3, 抽象类是一个特殊的类 ,比较灵活. 特殊在 : 抽象类里可以有抽象方法 , 也可以有普通方法 .//到底是普通方法还是抽象方法,看你要不要提供方法体了.abstract class Fu{//4, 这个方法是最终方法,不能被子类重写!!final public void eat() {System.out.println("爸爸在吃肉");}//1 , sleep被子类继承,并且发生了方法重写,也就是想改父类的方法体.---父类就不提供方法体了,就变成了抽象方法abstract public void sleep() ;abstract public void game() ;}//5 , 子类继承抽象类以后,可以 重写所有的抽象方法 , 否则 , 子类就包含着抽象方法是一个抽象子类class Zi extends Fu{@Overridepublic void sleep() {System.out.println("Zi...sleep()");}@Overridepublic void game() {System.out.println("Zi...game()");}}
4.4 抽象类的匿名子类
package .per;/** 抽象类的匿名子类:凡是匿名的作用都是为了省事,只用一次。* 匿名子类:不用在创建类继承抽象父类后再重写方法,直接在new对象的同时重写方法,简化了代码。* */public class PersonTest {public static void main(String[] args) {method(new Student());//普通类的匿名对象Worker worker = new Worker();method1(worker);//普通类的普通对象method1(new Worker());//普通类的匿名对象System.out.println("********************");//创建了一匿名子类的普通对象:p /** 省略class Worker extends Person{......}, * 直接创建对象的时候重写抽象父类的抽象方法即可,不用在单独的创建类继承后再重写抽象方法。*/Person p = new Person(){//注意:这里是创建了没有名字的子类对象,不是父类的对象Person。person是抽象类也不能够创建对象。@Overridepublic void eat() {System.out.println("吃东西");}@Overridepublic void breath() {System.out.println("好好呼吸");}};method1(p);System.out.println("********************");//创建匿名子类的匿名对象method1(new Person(){@Overridepublic void eat() {System.out.println("吃好吃东西");}@Overridepublic void breath() {System.out.println("好好呼吸新鲜空气");}});} //main方法的结束符public static void method1(Person p){p.eat();p.breath();}public static void method(Student s){}}class Worker extends Person{//用了匿名子类的对象这一步可省略@Overridepublic void eat() {}@Overridepublic void breath() {}}
package .per;/** abstract关键字的使用* 1.abstract:抽象的* 2.abstract可以用来修饰的结构:类、方法* * 3. abstract修饰类:抽象类* > 此类不能实例化*> 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)*> 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作* * * 4. abstract修饰方法:抽象方法* > 抽象方法只有方法的声明,没有方法体* > 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。*> 若子类重写了父类中的所有的抽象方法后,此子类方可实例化* 若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰*/public class AbstractTest {public static void main(String[] args) {//一旦Person类抽象了,就不可实例化//Person p1 = new Person();//p1.eat();}}abstract class Creature{public abstract void breath();}abstract class Person extends Creature{String name;int age;public Person(){}public Person(String name,int age){this.name = name;this.age = age;}//不是抽象方法://public void eat(){////}//抽象方法public abstract void eat();public void walk(){System.out.println("人走路");}}class Student extends Person{public Student(String name,int age){super(name,age);}public Student(){}public void eat(){System.out.println("学生多吃有营养的食物");}@Overridepublic void breath() {System.out.println("学生应该呼吸新鲜的没有雾霾的空气");}}
4.5 抽象类的应用:模板方法的设计模式
package com.atguigu.java;/** 抽象类的应用:模板方法的设计模式* */public class TemplateTest {public static void main(String[] args) {SubTemplate t = new SubTemplate();t.spendTime();}}abstract class Template{//计算某段代码执行所需要花费的时间public void spendTime(){long start = System.currentTimeMillis();this.code();//不确定的部分、易变的部分long end = System.currentTimeMillis();System.out.println("花费的时间为:" + (end - start));}public abstract void code();}class SubTemplate extends Template{@Overridepublic void code() {for(int i = 2;i <= 1000;i++){boolean isFlag = true;for(int j = 2;j <= Math.sqrt(i);j++){//开方if(i % j == 0){isFlag = false;break;}}if(isFlag){System.out.println(i);}}}}
4.6 不让外界实例化对象的方式(2种)
把你的类修饰成抽象类把你的构造方法私有化4.7 给成员变量赋值的方式(2种)
1.调用setXxx()
2.利用构造方法赋值(创建对象时会调用构造方法,在构造方法里面把产传递得值赋给成员变量)
原因:不直接赋值是因为写死了代码不灵活。
5 Day10–面向对象5
5.1 接口(default新特性)
5.1.1 概念
Java里面由于不允许多重继承,所以如果要实现多个类的功能,则可以通过实现多个接口来实现。
Java接口
和Java抽象类
代表的就是抽象类型,就是我们需要提出的抽象层的具体表现。OOP面向对象
的编程,如果要提高程序的复用率,增加程序的可维护性,可扩展性,就必须是面向接口的编程,面向抽象的编程,正确地使用接口、抽象类这些有用的抽象类型做为java结构层次上的顶层。
语法:
interface 接口名{代码… }abstract class 类名{代码…} //2单词变成了一个
5.1.2 特点
接口中的方法都是公共的抽象方法,变量都是公共的常量,没有构造方法(接口中的资源都是公共的)。通过interface关键字创建接口通过implements让子类来实现(抽象类是extends继承)可以理解成,接口是一个特殊的抽象类接口突破了java的单继承的局限性接口和类之间可以多实现,接口和接口之间可以多继承(“,”隔开),还能是继承的同时多实现,但是不能是同时多继承多实现 或 改变顺序先实现在继承。(总结:同种类型之间是继承,不同是实现关系。)接口是对外暴露的规则,是一套开发规范接口提高了程序的功能扩展,降低了耦合性(耦合性就是程序的关联性,关联性越低越好)注意:
接口继承接口怎么调用?因为接口是一个特殊的抽象类不能实例化,所以我们只能在创建一个普通的子类来实现这个接口,在实例化这个实现类。要注意重写接口里的抽象方法的权限,因为是默认为public 所以子类的权限至少等于为public(2同2小一大原则)。接口的default新特性: jdk从1.8开始接口中可以有普通方法(静态方法和默认方法),用static或者默认的default修饰(default不省略) 。接口存在普通方法的意义:如果抽象类有很多抽象方法那么子类都要重写太麻烦,用普通方法想重写就重写方便。子类继承抽象父类,重写父类方法的重写,叫方法的重写。
子类实现接口,重写接口方法的重写,叫方法的实现。当然你都叫做重写也没事,只是不精确。
5.1.3 入门案例1
package cn.tedu.interfacedemo;//接口的入门案例public class Test4_Inter {public static void main(String[] args) {//TODO 创建多态对象测试//5,接口可以创建对象吗??? --- 不能!!!和抽象类一样,不能实例化//Inter i = new Inter();Inter i = new InterImpl();i.delete();i.save();}}//创建接口//1,通过interface关键字定义接口interface Inter{//2,接口里可以有普通方法吗???---不可以!!!接口里都是抽象方法abstract public void delete();abstract public void save();}//3,实现类,想要使用接口里的功能,需要 实现 接口,用implements关键字//4, 实现类实现了接口以后,可以是一个普通实现类,就要求重写所有抽象方法class InterImpl implements Inter{//重写所有抽象方法(注意抽象方法有分号,没有方法体,连{}也没有)//重写的方法把它变为普通的方法,把abstract去掉,写方法体,注意分号。@Overridepublic void delete() {System.out.println("delete()...");}@Overridepublic void save() {System.out.println("save()...");}}//实现类一般都是在接口上加 Impl//6, 实现类实现了接口以后,如果没有全部重写抽象方法,就是个抽象子类abstract class InterImpl2 implements Inter{}
5.1.4 入门案例2
package com.atguigu.java1;/** 接口的使用* 1.接口使用interface来定义* 2.Java中,接口和类是并列的两个结构* 3.如何定义接口:定义接口中的成员* * 3.1 JDK7及以前:只能定义全局常量和抽象方法* >全局常量:public static final的.但是书写时,可以省略不写* >抽象方法:public abstract的* * 3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)* * 4. 接口中不能定义构造器的!意味着接口不可以实例化* * 5. Java开发中,接口通过让类去实现(implements)的方式来使用.* 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化* 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类* * 6. Java类可以实现多个接口 --->弥补了Java单继承性的局限性* 格式:class AA extends BB implements CC,DD,EE* * 7. 接口与接口之间可以继承,而且可以多继承* * ******************************** 8. 接口的具体使用,体现多态性* 9. 接口,实际上可以看做是一种规范* * 面试题:抽象类与接口有哪些异同?* */public class InterfaceTest {public static void main(String[] args) {System.out.println(Flyable.MAX_SPEED);System.out.println(Flyable.MIN_SPEED);//Flyable.MIN_SPEED = 2;Plane plane = new Plane();plane.fly();}}interface Flyable{//全局常量public static final int MAX_SPEED = 7900;//第一宇宙速度int MIN_SPEED = 1;//省略了public static final//抽象方法public abstract void fly();//省略了public abstractvoid stop();//Interfaces cannot have constructors//public Flyable(){////}}interface Attackable{void attack();}class Plane implements Flyable{@Overridepublic void fly() {System.out.println("通过引擎起飞");}@Overridepublic void stop() {System.out.println("驾驶员减速停止");}}abstract class Kite implements Flyable{@Overridepublic void fly() {}}class Bullet extends Object implements Flyable,Attackable,CC{@Overridepublic void attack() {// TODO Auto-generated method stub}@Overridepublic void fly() {// TODO Auto-generated method stub}@Overridepublic void stop() {// TODO Auto-generated method stub}@Overridepublic void method1() {// TODO Auto-generated method stub}@Overridepublic void method2() {// TODO Auto-generated method stub}}//************************************interface AA{void method1();}interface BB{void method2();}interface CC extends AA,BB{}
5.1.5 演示接口是一种规范和匿名实现类对象
接口的匿名实现类和抽象类的匿名子类用发相同。
package com.atguigu.java1;/** 接口的使用* 1.接口使用上也满足多态性* 2.接口,实际上就是定义了一种规范* 3.开发中,体会面向接口编程!* */public class USBTest {public static void main(String[] args) {Computer com = new Computer();//1.创建了接口的非匿名实现类的非匿名对象Flash flash = new Flash();com.transferData(flash);//2. 创建了接口的非匿名实现类的匿名对象com.transferData(new Printer());//3. 创建了接口的匿名实现类的非匿名对象USB phone = new USB(){@Overridepublic void start() {System.out.println("手机开始工作");}@Overridepublic void stop() {System.out.println("手机结束工作");}};com.transferData(phone);//4. 创建了接口的匿名实现类的匿名对象com.transferData(new USB(){@Overridepublic void start() {System.out.println("mp3开始工作");}@Overridepublic void stop() {System.out.println("mp3结束工作");}});}}class Computer{public void transferData(USB usb){//USB usb = new Flash();usb.start();System.out.println("具体传输数据的细节");usb.stop();}}interface USB{//常量:定义了长、宽、最大最小的传输速度等void start();void stop();}class Flash implements USB{@Overridepublic void start() {System.out.println("U盘开启工作");}@Overridepublic void stop() {System.out.println("U盘结束工作");}}class Printer implements USB{@Overridepublic void start() {System.out.println("打印机开启工作");}@Overridepublic void stop() {System.out.println("打印机结束工作");}}
5.2 接口的用法
5.2.1 构造方法
接口里是没有构造方法的,在创建实现类的对象时默认的super(),是调用的默认Object的无参构造。
public interface Inter2 {//接口里可以有构造方法吗???--没有!!,抽象类有// public Inter2() {}}
5.2.2 成员变量
接口里面的资源都是公共的,用public修饰。接口的变量默认都是静态常量。总结:接口中的变量都是公共的 静态 常量
。例:
public static final int age;等价于:int age; //接口中会默认自动拼接 public static final
5.2.3 接口的成员方法
接口里面的资源都是公共的,用public修饰。接口里面的方法,默认都是抽象的,即使不写也会默认拼接,只不过不会显示。总结:接口中的方法都是公共的 抽象 方法
,即使不写也会默认拼接,只不过不会显示。例:
public abstract void save();等价于:void save(); //会默认拼接 public abstract
5.3 接口的复杂用法
Java中单继承的局限性通过接口可以解决:接口可以多继承也可以多实现,甚至可以继承的同时多实现。
package cn.tedu.interfacedemo;//这个类用来测试接口的复杂用法:多继承多实现public class Test4_ComInter {public static void main(String[] args) {Interface1 in = new Interface1Impl();in.save();in.update();}}//创建接口1interface Interface1{void save();void update();}//创建接口2interface Interface2{void get();void delete();}//1、打破了java单继承的局限性,因为接口之间可以多继承,多个接口之间逗号隔开interface Interface3 extends Interface1,Interface2{void add();}//3、接口还可以多实现吗??---可以多实现,只不过接口之间逗号隔开class ManyImpl implements Interface1,Interface2{public void save() {}public void update() {}public void get() {}public void delete() {}}//4、接口可以继承的同时,多实现?? -- class MoreImple extends ManyImpl implements Interface1,Interface2 {}//注意:只能是先继承在多实现。顺序不能变,并且不能多继承多实现。//2、创建实现类,使用3号接口的功能,需要重写几个方法呢??---同时重写1号和2号和3号接口里的所有功能class Interface3Impl implements Interface3{@Overridepublic void save() {}@Overridepublic void update() {}@Overridepublic void get() {}@Overridepublic void delete() {}@Overridepublic void add() {}}//TODO 创建实现类class Interface1Impl implements Interface1{@Overridepublic void save() {System.out.println("save()...");}@Overridepublic void update() {System.out.println("update()...");}}
5.4 接口中的jdk1.8的新特性用法
类SubClassTest :
package com.atguigu.java8;public class SubClassTest {public static void main(String[] args) {SubClass s = new SubClass();//s.method1();//SubClass.method1();//知识点1:接口中定义的静态方法,只能通过接口来调用。CompareA.method1();//知识点2:通过实现类的对象,可以调用接口中的默认方法。//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法s.method2();//知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,//那么在实现类没有重写此方法的情况下,报错。-->接口冲突。//这就需要我们必须在实现类中重写此方法s.method3();}}class SubClass extends SuperClass implements CompareA,CompareB{public void method2(){System.out.println("SubClass:上海");}public void method3(){System.out.println("SubClass:深圳");}//知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法public void myMethod(){method3();//调用自己定义的重写的方法super.method3();//调用的是父类中声明的//调用接口中的默认方法CompareA.super.method3();CompareB.super.method3();}}
接口CompareA :
package com.atguigu.java8;/** * JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法* */public interface CompareA {//静态方法public static void method1(){System.out.println("CompareA:北京");}//默认方法public default void method2(){System.out.println("CompareA:上海");}default void method3(){System.out.println("CompareA:上海");}}
接口CompareB :
package com.atguigu.java8;public interface CompareB {default void method3(){System.out.println("CompareB:上海");}}
类SuperClass :
package com.atguigu.java8;public class SuperClass {public void method3(){System.out.println("SuperClass:北京");}}
5.5 接口的应用1:代理模式
package com.atguigu.java1;/** 接口的应用:代理模式* 以下代码为:静态代理模式* */public class NetWorkTest {public static void main(String[] args) {Server server = new Server();//server.browse();ProxyServer proxyServer = new ProxyServer(server);proxyServer.browse();}}interface NetWork{public void browse();}//被代理类class Server implements NetWork{@Overridepublic void browse() {System.out.println("真实的服务器访问网络");}}//代理类class ProxyServer implements NetWork{private NetWork work;public ProxyServer(NetWork work){this.work = work;}public void check(){System.out.println("联网之前的检查工作");}@Overridepublic void browse() {check();work.browse();}}
5.6 接口的应用2:工厂模式
工厂模式:实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
工厂:为了创建对象用的。
工厂模式的分类:
简单工厂模式:用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)工厂方法模式:用来生产同一等级结构中的固定产品。(支持增加任意产品)抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
5.7 初识设计模式
Java中有23 种设计模式,本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性,以及类的关联关系和组合关系的充分理解。
当然,软件设计模式只是一个引导,在实际的软件开发中,必须根据具体的需求来选择。
1、对于简单的程序,可能写一个简单的算法要比引入某种设计模式更加容易。
2、但是对于大型项目开发或者框架设计,用设计模式来组织代码显然更好。
5.7.1 单例设计模式概念
单例模式可以说是大多数开发人员在实际中使用最多的,常见的Spring默认创建的bean就是单例模式的。
单例模式有很多好处,比如可节约系统内存空间,控制资源的使用。
其中单例模式最重要的是确保对象只有一个。
简单来说,保证一个类在内存中的对象就一个。
RunTime就是典型的单例设计,我们通过对RunTime类的分析,一窥究竟。
5.7.2 源码剖析
/*** Every Java application has a single instance of class* <code>Runtime</code> that allows the application to interface with* the environment in which the application is running. The current* runtime can be obtained from the <code>getRuntime</code> method.* <p>* An application cannot create its own instance of this class.** @author unascribed* @seejava.lang.Runtime#getRuntime()* @since JDK1.0*/RunTime.javapackage java.lang;public class Runtime {//1、创建静态的全局唯一的对象private static Runtime currentRuntime = new Runtime();//2、私有构造方法, /** Don't let anyone else instantiate this class */private Runtime() {}//3、通过自定义的静态方法获取实例public static Runtime getRuntime() {return currentRuntime;}}
5.7.3 饿汉式
目的:控制外界创建对象的个数只能创建1个对象。
开发步骤:
1、私有化构造方法
2、在类的内部创建好对象
3、对外界提供一个公共的get(),返回一个已经准备好的对象。因为现在构造方法是私有的所以外界不能创建对象调用方法,只能声明为静态的方法通过类名进行调用。
4、没有线程安全问题
package cn.tedu.single;//测试单例设计模式public class Test8_Single {public static void main(String[] args) {Single s = Single.get();Single s1 = Single.get();//get()多少次,内存中使用的都是同一个对象System.out.println(s);//cn.tedu.single.Single@15db9742System.out.println(s1);//cn.tedu.single.Single@15db9742}}class Single{//1、私有化构造方法,不让外界直接newprivate Single() {}//2、在类的内部,创建好对象//static :静态只能调用静态,因为方法是静态的调用这对象属性也只能是静态的。static private Single s = new Single();//3、对外界提供一个公共的get(),返回一个已经准备好的对象//static是为了外界不通过对象访问而是通过类名直接方法static public Single get(){//注意:静态只能调用静态return s;}}
5.7.4 懒汉式
懒汉式有线程安全问题—》在day05–java高级编程—》同步锁中解决。
注意:面试时要求写一个单例设计模式:要么写饿汉式,要么写线程安全的懒汉式,不要写线程不安全的懒汉式。
class Single{//1、私有化构造方法,不让外界直接newprivate Single() {}//2、在类的内部,创建好对象//static :静态只能调用静态static private Single s = null;//3、对外界提供一个公共的get(),返回一个已经准备好的对象//static是为了外界不通过对象访问而是通过类名直接方法static public Single get(){//注意:静态只能调用静态if(s==null){/*会有线程安全问题:假如现在多个线程,每个线程调用run方法,各个run方法有调用这个get方法。当第一个线程进入到这个get方法后,第一次进入对象为空,此时可能发生阻塞第二个线程进来判断为空也需要创建对象。这样就创建了2个对象不合理。s相当于共享数据。*///如何解决?查看在day05--java高级编程---》同步锁中解决。s = new Single();}return s;//是null说明还没有创建对象,不是null说明创建好了对象直接返回,可以保证只创建一次对象。}}
5.7.5 饿汉式和懒汉式的区别
饿汉式:
坏处:对象加载时间过长。好处:饿汉式是线程安全的
懒汉式:
好处:延迟对象的创建。目前的写法坏处:线程不安全。—>到多线程内容时,再修改( 加上synchronized锁 )
5.7.6 单例设计模式-应用场景
5.8 拓展
5.8.1 abstract注意事项
抽象方法要求子类继承后必须重写。那么,abstract关键字不可以和哪些关键字一起使用呢?以下关键字,在抽象类中,用是可以用的,只是没有意义了。
1、private:被私有化后,子类无法重写,与abstract相违背。
2、static:静态的,优先于对象存在。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。
3、final:被final修饰后,无法重写,与abstract相违背。
5.8.2 接口和抽象类的区别
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法,如果要扩展抽象类的新方法,子类将很容易的就能得到这些新方法。
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
5.8.3 了解软件设计的开闭原则OCP
开放功能扩展,关闭源码修改等。
开闭原则的英文全称是Open Close Principle,缩写是OCP,它是Java世界里最基础的设计原则,它指导我们如何建立一个稳定的、灵活的系统。
开闭原则的定义是:软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的。
开闭原则,是一种设计模式,随着面向对象程序设计的思想,应运而生。
开,指的是可以在源代码的基础上进行扩展,比如继承,接口,抽象类等。在JAVA中,之所以用继承,是在可以直接调用类库的前提下,对其功能进行扩展。不需要应用者去了解封装类的内部逻辑就可以做开发。
闭:指不允许对原有的代码进行修改。以免影响其他现有功能,造成功能瘫痪。
5.8.4 打桩和DeBug
当程序的运行结果和你说预期的结果不同时,如何调试代码?
(1)打桩: System.out.println(数据);
(2)Debug调试工具:略,详情查看博客杂谈分栏下。
5.8.5 Math类中提供了三个取正有关的方法:ceil,floor,round
写法:
System.out.println(Math.ceil(11.5));//12.0
(1)ceil的英文意义是天花板,该方法表示向上取整
(2)floor的英文意义是地板,该方法就表示向下取整
(3)round方法:它表示“四舍五入”(即: +0.5向下取整)
6 异常
6.1 异常概述
出现背景:
在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美
,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅
等等。
异常:
在Java语言中,将程序执行中发生的不正常情况称为"异常"。(开发过程中的语法错误和逻辑错误不是异常)
6.2 异常体系
说明:
Throwable
:在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)Error
:严重问题,不需要处理Exception
:称为异常类,它表示程序本身可以处理的问题RuntimeException
:在编译期是不检查的,出现问题后,需要我们回来修改代码非RuntimeException
:编译期就必须处理的,否则程序不能通过编译,就更不能正常运行了
6.2.1 Error举例
package com.atguigu.java;/** Error:* Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。* * 一般不编写针对性的代码进行处理。出现问题只能改代码。* * */public class ErrorTest {public static void main(String[] args) {//1.栈溢出:java.lang.StackOverflowError//main(args);//2.堆溢出:java.lang.OutOfMemoryError Integer[] arr = new Integer[1024*1024*1024];}}
6.2.2 Exception举例
package com.atguigu.java1;import java.io.File;import java.io.FileInputStream;import java.util.Date;import java.util.Scanner;import org.junit.Test;/** 一、异常体系结构* * java.lang.Throwable* |-----java.lang.Error:一般不编写针对性的代码进行处理。* |-----java.lang.Exception:可以进行异常的处理* |------编译时异常(checked)* |-----IOException* |-----FileNotFoundException* |-----ClassNotFoundException* |-----SQLException sql异常* |------运行时异常(unchecked,RuntimeException)* |-----NullPointerException* |-----ArrayIndexOutOfBoundsException* |-----ClassCastException* |-----NumberFormatException 数字格式异常* |-----InputMismatchException 输入不匹配异常* |-----ArithmeticException 算术异常* * * * 面试题:常见的异常都有哪些?举例说明*/public class ExceptionTest {//******************以下是编译时异常***************************@Testpublic void test7(){//File file = new File("hello.txt");//FileInputStream fis = new FileInputStream(file);////int data = fis.read();//while(data != -1){//System.out.print((char)data);//data = fis.read();//}////fis.close();}//******************以下是运行时异常***************************//ArithmeticException@Testpublic void test6(){int a = 10;int b = 0;System.out.println(a / b);}//InputMismatchException@Testpublic void test5(){Scanner scanner = new Scanner(System.in);int score = scanner.nextInt();System.out.println(score);scanner.close();}//NumberFormatException@Testpublic void test4(){String str = "123";str = "abc";int num = Integer.parseInt(str);}//ClassCastException@Testpublic void test3(){Object obj = new Date();String str = (String)obj;}//IndexOutOfBoundsException@Testpublic void test2(){//ArrayIndexOutOfBoundsException//int[] arr = new int[10];//System.out.println(arr[10]);//StringIndexOutOfBoundsExceptionString str = "abc";System.out.println(str.charAt(3));}//NullPointerException@Testpublic void test1(){//int[] arr = null;//System.out.println(arr[3]);String str = "abc";str = null;System.out.println(str.charAt(0));}}
6.3 JVM的默认处理方案
如果程序出现了问题,我们没有做任何处理,最终JVM会做默认的处理
把异常的名称,异常原因及异常出现的位置等信息输出在了控制台(由下图可知)程序停止执行(由下图可知,结束
并没有输出到控制台)
6.4 Throwable的成员方法
6.5 编译时异常和运行时异常的区别
Java中的异常被分为两大类:编译时异常
和运行时异常
,也被称为受检异常
和非受检异常
所有的RuntimeException类及其子类被称为运行时异常,其他的异常都是编译时异常
编译时异常: 必须显示处理,否则程序就会发生错误,无法通过编译运行时异常: 无需显示处理,也可以和编译时异常一样处理
6.6 异常处理概述
为什么要进行异常处理:
因为java虚拟机的默认处理方案,会让程序在出现异常的地方直接结束掉。而在实际开发中我们程序某一个部分出现问题了,它不应该影响后续的执行,所以我们要自己处理异常。
如果程序出现了问题,我们需要自己来处理,有两种方案:
try … catch …throws
异常处理:抓抛模型
过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个异常对象,并将对象抛出。一旦抛出异常对象后,其后的代码就不会再执行。
关于异常对象的产生:
① 系统自动生成的异常对象
② 手动的生成一个异常对象,并抛出(throw)过程二:“抓”:可以理解为异常的处理方式: try-catch-finally :真正的处理异常,后续代码会执行
throws:没有真正处理异常仅仅是抛给调用者,后续代码不会执行
如果程序出现了异常没有处理:
最终会跑到main方法中,main方法由jvm虚拟机管理,jvm虚拟机处理异常是杀死进程,结束掉程序。
异常处理的意义:
程序出现异常后仍能正确执行,不影响程序运行。
6.7 异常处理方式一:(try-catch-finally)
说明:
try-catch-finally,又叫做捕获方式。
语法:
//try只能而且出现一次 catch可以出现0到N次 finally出现0到1次 try{//可能出现异常的代码}catch(异常类型1 变量名1){//处理异常的方式1}catch(异常类型2 变量名2){//处理异常的方式2}catch(异常类型3 变量名3){//处理异常的方式3}....finally{//一定会执行的代码}
执行流程:
程序从try里面的代码开始执行出现异常,会自动生成一个异常类对象
,该异常对象将被提交给Java运行时系统当Java运行时系统接收到异常对象时,会到catch中去找匹配的异常类,找到后进行异常的处理执行完毕之后,程序还可以继续往下执行
。
6.7.1 测试1:try-catch结构
package com.shuai;/*** 1.finally是可选的。* * 2.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常就会生成一个对应类的异常对象,根据* 此对象的类型到catch中进行匹配。* * 3.一旦try中的异常对象匹配到一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出当前的* try-catch结构(在没有写finally的情况),继续执行其后的代码。* * 4.catch中的异常类型如果没有父子类关系,谁声明在上,,谁声明在下无所谓。* catch中的异常类型如果有父子类关系,则要求子类在上父类在下,从小到大的排序,否则报错。* * 5.常用的异常处理方式:1)String e.getMessage() 2)void e.printStackTrace() 这个比较常用* * 6.在try结构中定义的变量,出了try结构以后就不能使用了。想要使用把变量声明在外面,赋值在结构里面。* * 7.为什么不写成int num;而要多赋值个0,首先因为变量想要输出首先要声明好后并且有初始化的值,而局部变量* 没有默认值,一旦try中的程序报错,直接输出num,num没有值程序会报错,为了避免这种情况,加上0不会影响程* 序也不会因为异常情况导致num没有值而保错。* *8.try-catch-finally也可以嵌套* 体会1:使用try-catch-finally结构处理编译异常时,使得编译时不会报错,但运行时有可能报错,相当于try-catch-finally* 将编译时可能出现的异常延迟到运行时出现。* 体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。* 针对于编译时异常,我们说一定要考虑异常的处理。(如果与前端页面交互提示时,仍要处理。)*/public class Demo05 {public static void main(String[] args) {String str="123";str="abc";/*为什么不写成int num;而要多赋值个0,首先因为变量想要输出首先要声明好后并且有初始化的值,而局部变量没有默认值,一旦try中的程序报错,直接输出num,num没有值程序会报错,为了避免这种情况,加上0不会影响程序也不会因为异常情况导致num没有值而报错。*/int num = 0;try {num=Integer.parseInt(str);//int num=Integer.parseInt(str);//出现异常System.out.println("hell0-------01");//不会输出} catch (NullPointerException e) {//习惯上异常名叫 eSystem.out.println("出现了空指针转换异常");}catch (NumberFormatException e) {//一个类型e只在一个catch中有效,所以不同的catch中名字可以相同。//System.out.println("出现了数值转换异常"); 一般不这样写,用异常类方法代替,如下://String e.getMessage():此方法返回值是String类型,想要看就要输出它。因为有返回值就要定义变量进行接收,查看就要输出,这里简写一块了。// System.out.println(e.getMessage()); //打印异常信息//void e.printStackTrace():此方法返回值是void类型,不需要在进行输出就能查看。e.printStackTrace(); //打印异常详细信息}catch (Exception e){System.out.println("出现了异常");//不会输出}//System.out.println(num);报错不能调用,因为变量num在catch中定义的,作用域在catch中。System.out.println(num);System.out.println("hell0-------02");//会输出}}
6.7.2 测试2:finally使用场景分析
package com.atguigu.java1;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import org.junit.Test;/** try-catch-finally中finally的使用:* * * 1.finally是可选的* * 2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有* return语句等情况。* * 3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的* 释放。此时的资源释放,就需要声明在finally中。* * * */public class FinallyTest {@Testpublic void test2(){FileInputStream fis = null;try {File file = new File("hello1.txt");fis = new FileInputStream(file);int data = fis.read();while(data != -1){System.out.print((char)data);data = fis.read();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally{try {if(fis != null)//如果文件没有创建成功为赋的默认值null会报空指针异常,加上if判断避免这一情况。fis.close();} catch (IOException e) {e.printStackTrace();}}}@Testpublic void testMethod(){int num = method();System.out.println(num);}public int method(){try{int[] arr = new int[10];System.out.println(arr[10]);return 1;}catch(ArrayIndexOutOfBoundsException e){e.printStackTrace();return 2;}finally{System.out.println("我一定会被执行");return 3;}}@Testpublic void test1(){try{int a = 10;int b = 0;System.out.println(a / b);}catch(ArithmeticException e){e.printStackTrace();//int[] arr = new int[10];//System.out.println(arr[10]);}catch(Exception e){e.printStackTrace();}//System.out.println("我好帅啊!!!~~");finally{System.out.println("我好帅啊~~");}}}
快捷键:选中报错包裹的代码—>Suround With—>Try/catch Block
注意:修改代码关闭流放在finally中。
6.8 异常处理方式二:(throws)
使用背景:
虽然我们通过try…catch.….可以对异常进行处理,但是并不是所有的情况我们都有权限进行异常的处理也就是说,有些时候可能出现的异常是我们处理不了的,这个时候该怎么办呢?
针对这种情况,Java提供了throws的处理方案
格式:
throws 异常类名;
注意:这个格式是跟在方法的括号
后面的
编译时异常必须要进行处理,两种处理方案: try…catch 或者throws,如果采用throws这种方案,将来谁调用谁处理
运行时异常可以不处理,出现问题后,需要我们回来修改代码
执行流程:
"throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!
6.8.1 案例1:测试throws关键字
package com.atguigu.java1;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;/** 异常处理的方式二:throws + 异常类型** 1. "throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。*一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常*类型时,就会被抛出。异常代码后续的代码,就不再执行!** 2. 体会:try-catch-finally:真正的将异常给处理掉了。* throws的方式只是将异常抛给了方法的调用者。 并没有真正将异常处理掉。** 3. 开发中如何选择使用try-catch-finally 还是使用throws?* 3.1 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果* 子类重写的方法中有异常,必须使用try-catch-finally方式处理。* 3.2 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws* 的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。**/public class ExceptionTest2 {public static void main(String[] args){try{method2();}catch(IOException e){e.printStackTrace();}//method3();}public static void method3(){try {method2();} catch (IOException e) {//IOException 是FileNotFoundException异常的父类,这里处理方式又相同所以只需要写一个catch即可。e.printStackTrace();}}//method1抛出了2个异常,这里method2调用为什么只抛出一个异常呢??//如果2个异常是父子类关系,并且处理异常的方式相同,比如都是e.printStackTrace();,那么只需要抛出一个异常即可。public static void method2() throws IOException{method1();}public static void method1() throws FileNotFoundException,IOException{File file = new File("hello1.txt");FileInputStream fis = new FileInputStream(file);int data = fis.read();while(data != -1){System.out.print((char)data);data = fis.read();}fis.close();System.out.println("hahaha!");}}
6.8.2 案例2:测试方法重写子类抛出异常小于父类
方法重写的规则之一::子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
package com.atguigu.java1;import java.io.FileNotFoundException;import java.io.IOException;/** 方法重写的规则之一:* 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型* * */public class OverrideTest {public static void main(String[] args) {OverrideTest test = new OverrideTest();test.display(new SubClass());}public void display(SuperClass s){try {s.method();} catch (IOException e) {e.printStackTrace();}}}class SuperClass{public void method() throws IOException{}}class SubClass extends SuperClass{//如果方法重写调用子类的方法的方法体,子类抛出的异常大于父类的,父类已经处理的异常在子类中不包含,则父类处理异常没有什么意义。public void method()throws FileNotFoundException{}}
6.9 手动抛出异常对象(throw)
使用背景:如下图。
作用:用于在方法体内部抛出异常对象
关于异常对象的产生:
系统自动生成的异常对象手动的生成一个异常对象,并抛出(throw)
注意:手动抛出异常如果是运行时异常对象可以不用处理,如果抛出的是编译期异常对象一定要处理异常。
6.9.1 没有手动抛出异常对象之前
package com.atguigu.java2;public class StudentTest {public static void main(String[] args) {Student s = new Student();s.regist(-1001);System.out.println(s);//直接输出对象的引用实际上时输出.toString()方法。}}class Student{private int id;public void regist(int id) {if(id > 0){this.id = id;}else{/** 即便你此时输入的是负数不合理,只能给你个提示输入的数据错误,但是这样写还是把结果输出了。* 正常来讲:应该是输入的是负数不合理,程序直接报错不会再向下执行输出结果,所以这里一般是手动抛出异常对象,* 一旦数据不合理直接报异常,程序停止运行。*/System.out.println("您输入的数据非法!");}}@Overridepublic String toString() {return "Student [id=" + id + "]";}}
6.9.2 手动抛出异常对象之后
package com.atguigu.java2;public class StudentTest {public static void main(String[] args) {try {Student s = new Student();s.regist(-1001);System.out.println(s);} catch (Exception e) {//e.printStackTrace();/*throw new RuntimeException("您输入的数据非法!")里面的值是由RuntimeException的有参构造方法中super(xxx)调用--->* 父类的Exception的有参构造方法中super(xxx)调用----> Throable中的有参构造方法,在Throable类中通过有参构造方法* 赋值(this.detailMessage = message;) 给成员变量: private String detailMessage;为:您输入的数据非法!* 即:值最终赋值给Throable类的成员变量detailMessage。* * void e.getMessage() 底层源码:* public String getMessage() {*return detailMessage;* }* 这个方法getMessage()的返回值为Throable类中的detailMessage成员变量,所以输出结果为:您输入的数据非法! */System.out.println(e.getMessage());}}}class Student{private int id;public void regist(int id) throws Exception {if(id > 0){this.id = id;}else{//System.out.println("您输入的数据非法!");//手动抛出异常对象//throw new RuntimeException("您输入的数据非法!");抛出运行时异常,调用方不用处理。//throw new Exception("您输入的数据非法!");Exception包含编译器异常,所以一旦throws抛出,在调用方一定要进行处理编译器异常。throw new MyException("不能输入负数");//抛出的是自定义异常//错误的 String不是异常类//throw new String("不能输入负数");//return 0;如果方法有返回值且不影响结果,则可以用throw代替return,因为如果报异常也会结束方法运行。}}@Overridepublic String toString() {return "Student [id=" + id + "]";}}
6.9.3 throw和throws的区别
6.10 自定义异常
使用背景:
目的是系统提供的异常类型是有限的
,如果程序产生的异常不在提供中,可以抛出自己定义的异常使得异常类型更加精确
(一般和throw连用)一般这个自定义异常类,定义这个类时要做到见名知意
。
格式:
范例:
6.10.1 测试
和案例6.9.2连用。
package com.atguigu.java2;/** 如何自定义异常类?* 1.继承于现有的异常结构:RuntimeException 、Exception(包含运行和编译器异常,* 所以编译期就要进行处理异常)* RuntimeException异常:只需要继承RuntimeException异常或者它的子类。* Checked异常:只需要继承Exception异常或者Exception的子类,但需要除RuntimeException之外。* 2.提供全局常量:serialVersionUID* 3.提供重载的构造器* */public class MyException extends Exception{static final long serialVersionUID = -7034897193246939L;//无参构造方法public MyException(){}//有参构造方法public MyException(String msg){/** 调用父类的有参构造,因为上面的案例中处理异常使用的是e.getMessage()方法,* 这个方法返回值为Throable的成员变量dtailMessage属性。而现在自定义异常对象* 的通过有参构造方法创建对象并传参,这个自定义异常对象的参数不是自己定义的而是定义* 在顶级父类Throable中的成员变量,所以想要赋值给父类Throable的属性需要在子类* 的构造方法中通过super一步一步调用父类构造方法,最终在Throwable的构造方法中把* 值赋值给成员变量dtailMessage。*/super(msg);}}
7. 内部类
7.1 成员内部类(静态/非静态)
package com.atguigu.java2;/** 类的内部成员之五:内部类* 1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类* * 2.内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)* * 3.成员内部类:* 一方面,作为外部类的成员:* >调用外部类的结构* >可以被static修饰* >可以被4种不同的权限修饰* * 另一方面,作为一个类:* > 类内可以定义属性、方法、构造器等* > 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承* > 可以被abstract修饰* * * 4.关注如下的3个问题* 4.1 如何实例化成员内部类的对象* 4.2 如何在成员内部类中区分调用外部类的结构* 4.3 开发中局部内部类的使用 见《InnerClassTest1.java》* */public class InnerClassTest {public static void main(String[] args) {//创建Dog实例(静态的成员内部类):Person.Dog dog = new Person.Dog();dog.show();//创建Bird实例(非静态的成员内部类)://Person.Bird bird = new Person.Bird();//错误的Person p = new Person();Person.Bird bird = p.new Bird();bird.sing();System.out.println();bird.display("黄鹂");}}class Person{//外部类String name = "小明";int age;public void eat(){System.out.println("人:吃饭");}//静态成员内部类static class Dog{String name;int age;public void show(){System.out.println("卡拉是条狗");//eat();}}//非静态成员内部类class Bird{String name = "杜鹃";public Bird(){}public void sing(){System.out.println("我是一只小小鸟");Person.this.eat();//调用外部类的非静态属性 属性不同名可以省略为:eat();eat();System.out.println(age);}public void display(String name){System.out.println(name);//方法的形参System.out.println(this.name);//内部类的属性System.out.println(Person.this.name);//外部类的属性 属性不同名可以省略}}public void method(){//局部内部类class AA{}}{//局部内部类class BB{}}public Person(){//局部内部类class CC{}}}
7.2 局部内部类(匿名/非匿名)
package com.atguigu.java2;public class InnerClassTest1 {//开发中很少见public void method(){//局部内部类class AA{}}//返回一个实现了Comparable接口的类的对象public Comparable getComparable(){//创建一个实现了Comparable接口的类:局部内部类//方式一://class MyComparable implements Comparable{ //Comparable为源码里面的接口////@Override//public int compareTo(Object o) {//return 0;//}////}////return new MyComparable();//方式二:return new Comparable(){@Overridepublic int compareTo(Object o) {return 0;}};}}
day03--java基础编程:面向对象 构造方法 代码块讲解 this super static final 访问修饰符 方法重写 向上_下造型 main方法 抽象类 接口 设计模式 异常 内部类