1、语法
语法:包、权限修饰符
什么是包?
- 包是用来分门别类的管理各种不同类的,类似于文件夹,建包更利于程序的管理和维护。
- 建包的语法格式:package公司域名倒写.技术名称。建包建议全部英文小写,且具备意义。
- 舰爆语句必须在第一行,一般IDEA工具会帮助创建
package com.itheima.d1_package.it;
导包:
- 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!
- 导包格式:import 包名.类名。
public static void main(String[] args) { //目标:理解以下两点 //1、同一个包下的类,互相可以直接访问 System.out.println(User.onlineNumber); //2、不同包下的类。必须先导包才可以访问。 Student s = new Student(); Scanner sc = new Scanner(System.in); //3、如果这个类中使用不同包下的相同的类名, // 此时默认只能导入一个类的包。另一个类要使用全名访问。 com.itheima.d1_package.it2.Student s1 = new com.itheima.d1_package.it2.Student(); }
使用包来组织类
包解决两个问题:
1、组织管理不同的类
2、为包中的类提供了一个命名空间
创建包名时(域名发过来创建):
反着写。www.P04.com——>com.p04.包名
什么是权限修饰符?
- 权限修饰符:是用来控制一个成员能够被访问的范围。
- 可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。
权限修饰符的分类和具体作用范围:
- 权限修饰符:有四种作用范围从小到大(private->缺省->protected->public)
访问修饰符 自身 子类 同包 不同包 public ( 公共的 ) √ √ √ √ protected ( 受保护的) √ √ √ × default(默认的) (使用不多)(缺省) √ × √ × private (私有的) √ × × × 1、定义私有的成员:pricate 只能本类中访问
2、定义缺省修饰的成员:只能本类中、同包下其他类访问(包访问权限)
3、protected修饰的方法:本类,同包的其它类中,其他包的子类中。
4、public修饰的方法:本类,同包的其它类中,其他包的子类中,其他包的无关类中。
学完权限修饰符需要具备如下能力:
- 能识别别人定义的成员的访问范围
- 自己定义成员(方法,成员变量,构造器等)一般需要满足如下要求:成员变量一般私有。方法一般公开。 该成员只希望本类访问,使用private修饰。
总结: 凡是父类的属性都应该是 protected;凡是成员变量(属性) 都应该是private
语法:final的语法
final关键字是最终的意思,可以修饰(类、方法、变量)
修饰类:表明该类是最终类,不能被继承。
修饰方法:表明该方法是最终方法,不能被重写。
修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次。)
注意:
final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的。
java 常见的final的类
- String
- 8个基本数据类型的 包装类
- 等等
目标:理解final修饰变量的作用:
总规则:变量有且仅能被赋值一次。(理解语法)
变量有几种
1、局部变量
2、成员变量分为实例成员变量;静态成员变量
语法:常量、常量在开发里的作用
常量是使用了public static final修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变
常量的作用和好处:
- 可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性。
常量命名规范:
- 英文单词全部大写,多个单词下划线连接起来。
常量的执行原理:
- 在编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量;
- 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的。
常量做信息标志和分类:
- 代码可读性好,实现了软编码形式
语法:枚举、枚举作用
枚举的概述
- 枚举是Java中的一种特殊类型
- 枚举的作用:"是为了做信息的标志和信息的分类"。
- 选择常量做信息标志和分类:虽然可以实现可读性,但是入参值不受约束,代码相对不够严谨。
枚举做信息标志和分类:代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术!
定义枚举类的格式:
枚举的第一行必须罗列枚举类的对象名称,建议全部大写。
反编译后观察枚举的特征:
枚举的特征:
- 枚举类都是继承了枚举类型:java.lang.Enum
- 枚举都是最终类,不可以被继承。
- 构造器都是私有的,枚举对外不能创建对象。
- 枚举类的第一行默认都是罗列枚举对象的名称的。
- 枚举类相当于是多例模式。
2、抽象类
抽象
把相同的代码抽取出来,便于功能的重用,把不同的代码也抽取出来,便于功能的扩展
抽象类概述
抽象的实现 使用关键字: abstract
抽象类基本作用:
作为父类,用来被继承的。
abstract可以使用的位置:
修饰类 : 表示该类是一个抽象类. (子类如果继承该抽象类, 子类必须重写该抽象类的 抽象方法)
修饰方法: 表示该方法是一个抽象方法. (只需方法的声明(定义),不需要进行方法的实现)
修饰符 abstract class 类名{ 修饰符 abstract 返回值类型 方法名称 (形参列表); } public abstract class Animal { public abstract void run(); }
注意:
- 抽象方法:有abstract修饰 只能有方法签名,不能写方法体代码。
- 如果一个类中有抽象方法, 那么该类也必须为抽象类。否则报错。
- 一旦类被abstract 修饰, 表示该类是不能被实例化的。
使用场景:
- 抽样类可以理解成不完整的设计图,一般作为父类,让子类来继承。
- 当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式,具体实现交给子类去完成。此时这个类就可以声明成抽象类。
继承抽象类有哪些注意?
- 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
代码演示:
动物类(父类/基类):
/** *抽象类:有abstract修饰 */ public abstract class Animal { public String name; /** 抽象方法:有abstract修饰 不能写方法体代码 */ public abstract void run(); public String getName() { return name; } public void setName(String name) { this.name = name; } }
狗类(子类):
public class Dog extends Animal{ @Override public void run() { System.out.println("狗跑的贼快--"); } }
测试类:
public class Test { public static void main(String[] args) { //目标:认识抽象类,在了解它的使用场景 //使用场景:抽样类可以理解成不完整的设计图,一般作为父类,让子类来继承。 //当父类知道子类一定要完成某些行为,但是每个子类该行为的实现又不同,于是该父类就把该行为定义成抽象方法的形式 // ,具体实现交给子类去完成。此时这个类就可以声明成抽象类。 Dog d = new Dog(); d.run();//狗跑的贼快-- } }
抽象类案例:
需求:
- 某加油站推出了2中支付卡,一种是预存10000的金卡,后续加油享受8折优惠,另一种是预存5000的银卡,后续加油享受8.5折优惠。
- 请分别实现2种卡片进入收银系统后的逻辑,卡片需要包含主人名称,余额,支付功能。
分析实现:
创建一个卡片父类,定义属性包括主人名称、余额、支付功能(具体实现交给子类)
创建一张白金卡类:重写支付功能,按照原价的8折计算输出。
创建一张银卡类:重写支付功能,按照原价的8.5折计算输出。
代码实现:
卡片父类:
public abstract class Card { private String userName; private double money; /** 定义一个支付方法:表示卡片可以支付。 抽象方法 */ public abstract void pay(double money2); public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } }
金卡、银卡子类:
public class GoldCard extends Card { @Override public void pay(double money2) { System.out.println("您当前消费:" + money2); System.out.println("您卡片当前余额是:" + getMoney()); //优惠价: double rs = money2 * 0.8; System.out.println(getUserName() + "您实际支付:" + rs); //更新账户余额 setMoney(getMoney() - rs); } }
public class SilverCard extends Card{ @Override public void pay(double money2) { System.out.println("您当前消费:" + money2); System.out.println("您卡片当前余额是:" + getMoney()); //优惠价: double rs = money2 * 0.85; System.out.println(getUserName() + "您实际支付:" + rs); //更新账户余额 setMoney(getMoney() - rs); } }
测试类:
public class Test { public static void main(String[] args) { //目标:学习一下抽象类的基本使用,做一个父类,被继承,重写抽象方法。 GoldCard c = new GoldCard();//实例化金卡类 c.setMoney(10000); c.setUserName("张三"); c.pay(300); System.out.println("剩余:" + c.getMoney());//剩余:9760.0 SilverCard s = new SilverCard(); s.setMoney(5000); s.setUserName("李四"); s.pay(300); System.out.println("剩余:" + s.getMoney());//剩余:4745.0 } }
抽象类的特征、注意事项:
- 类有的成员(成员变量、方法、构造器)抽象类都具有。
- 抽象类里不一定有抽象方法,有抽象方法的类一定是抽象类。
- 一个类继承了抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
- 不能用abstract修饰变量、代码块、构造器
- 面试题:最重要的特征:得到了抽象方法,失去了创建对象的能力(有得有失)为什么?
Animal a = new Animal() ;
反证法:假如抽象类可以创建对象
a.run(); //run 方法连方法体都没有,因此抽象类不能创建对象
final和abstract关系:
- 互斥关系。
- abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
- 抽象方法定义通用功能让子类重写,final定义的方法子类不能重写。
面向对象的三大特性
封装 private 好处: 把功能的实现给隐藏起来, 达到安全的目的, 并且增强程序的控制性
继承 extends 好处: 代码的复用性,子类可以扩展自己的属性和方法
多态 父类的引用指向子类的对象 好处: 可以降低代码的耦合度, 并丰富代码的扩展性
有时会被称为四大特性
抽象 abstract 好处: 强制要求子类重写父类的方法. 父类没有实例化的必要.
抽象类的应用知识:模板方法模式
什么时候使用模板方法模式
- 使用场景说明:当系统里出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。
模板方法模式实现步骤
- 把功能定义成一个所谓的模板方法,放在抽象类里,模板方法里只定义通用且能确定的代码。
理解模板方法:写作文案例
需求:
- 现在有两类学生,一类是中学生,一类是小学生,他们都要写一篇作文(我的爸爸)。
- 要求每种类型的学生,标题的第一段和最后一段,内容必须是一样的。正文部分自己发挥。
- 请选择最优的面向对象方案进行设计。
代码:
学生类(父类):
public abstract class Student { /** 正式:声明了模板方法 */ public final void write() { System.out.println("\t\t\t\t\t《我的爸爸》"); System.out.println("你的爸爸是啥样,来说说:"); //正文部分(每个子类都要写的,但是每个子类写的情况不一样 // 因此,模板方法把正文部分定义成抽象方法,交给 // 具体的子类来完成) System.out.println(writeMain()); System.out.println("我的爸爸简直太好了--"); } public abstract String writeMain(); }
小学生、中学生(子类):
public class StudentChild extends Student{ @Override public String writeMain() { //正文 return "我的爸爸太牛b了,他总是买东西给我吃。。"; } }
public class StudentMiddle extends Student{ @Override public String writeMain() { return "我的爸爸也很牛b了,开车都不看红绿灯的。。"; } }
测试类:
public class Test { public static void main(String[] args) { //目标:理解模板方法模式的思想和使用步骤 Student s = new StudentMiddle(); s.write(); Student s2 = new StudentChild(); s2.write(); } }
抽象总结
这里的抽象其实是一个狭义的抽象,专指的abstract。
我们把子类里共有行为和共有属性不断的往父类进行抽取,那么当我们抽取到某一个层度的时候,会发现在父类里只能确定这个类有这个方法,无法确定这个方法是如何实现的了。而一个拥有抽象方法的类也不应该产生对象了(因为对象是具体的实现的存在,它当中所有的内容都应该是确定的),这样的类也就成了抽象类。
结论:有抽象方法的类一定是抽象类;
抽象类不能够产生对象;
如果一个类继承抽象类不去实现它的抽象方法,那么这个类也是抽象类。
抽象类除了不能产生对象,它的本质没有改变,一个普通类该有的它都有。它有属性、构造、、方法、代码块,甚至内部类。它也是单继承的。它就是在我们设计层次的上层,用来规范子类内容的。唯一的缺点就是”单继承“。
为了解决这个单继承的问题,提出了”接口“的概念。
1、接口是Java里大家学到的第三种引用数据类型;(类、数组);
Java里面核心的引用数据类型就它们三个,以后还会接触到的:枚举、注Lambda、记录。。。。。。
2、接口的定义
interface关键字;
接口的属性只能是公共的静态常量;
接口是没有构造方法的,所以接口不能直接产生对象;
长久以来,接口中我们最关注的是方法,而且是公共的抽象方法;大部分需求都是书写这种方法。直到jdk1.8开始才给接口添加了各种可以实现的方法,包括default方法和static方法。但其实这两种方法是属于”锦上添花“的设计,在99.99%的情况下,我们设计接口的目的都是去书写抽象方法。这个跟接口的作用有关系。
接口的作用:让没有继承关系的类也能共享行为。
3、接口的使用
3-1接口可以多继承接口;
3-2类可以多实现接口。
4、面向对象编程逐步过渡到面向接口编程;甚至很多设计人员在设计的时候不去设计抽象类咯,拿着就设计接口。这个是实际使用者偷懒的地方,其实在面向对象概念里,这两个东西是有原则性区别的。
一个抽象方法到底设计到抽象类当中还是接口当中是有规则的。比如:门。有各种各样的门,门也有各种各样的行为,不同的行为也是各有不同的。比如:开门 关门 锁 开锁 按门铃 通过猫眼看 。那么所有的这些行为都应该写成抽象方法。
那么我们观察这六个方法,我们可以根据自然的生活经验讲这六个方法分级别。很明显,在这六个方法当中有两个方法是门不可或缺的,就是开和关门,没有这两个方法,就不是门了。这两个行为就叫做门所谓的”与生俱来的行为“;
门必须要有的行为(与生俱来的行为要定义在抽象类里,表达了一种继承的传递;) 而锁,开锁,猫眼和按门铃,不是门对象必须的,它们属于附属添加的行为, 那么这种行为就要定义在接口里,供门的实现类各自选择。 定义接口的时候,尽量定义小接口,而不是大而全的接口。 除非某几个方法是同时出现或同时不出现的。那么才在一个接口里定义这多个方法。 这是面向对象设计原则中的“最小接口原则”,也叫“接口隔离原则” 如果一旦定义了大而全的接口,会导致实现类可能拥有它并不具备的方法 所以小接口可以隔离实现类拥有不必要的方法。 所以设计上不要让上层接口的设计问题导致下层实现类的不干净,也叫“接口隔离” 接口在命名的时候,包括后面学到的jdk中设计的接口,它们通常被叫做***able,代表的就是提供某额外的能力(功能)。设计是没有对错的;只要能完成当前的功能任务,那么你就不能说这个设计是错的;但是设计是有优劣的,更符合场景的设计会带来更多的好处。包括:代码量的减少,修改量的减少,可以更好的随着业务场景的变化而发生变化(不用推倒已有代码重新来
3、接口
概述
它是一种抽象类型, 它是一些抽象方法的集合. 你可以把它认为是一种特殊的抽象类。
接口的定义和特点:
什么是接口?接口是一种规范。规范一定是公开的。
注意:
由于接口体现规范思想,规范默认一定是公开的,所有代码层面,常量里的public static final可以省略不写;抽象方法里的public abstract可以省略不写。
接口的格式如下:
接口用关键字interface来定义
public interface 接口名{ //常量 //抽象方法 }
理解接口中的成分特点:
JDK 8 之前接口中只能有抽样方法和常量(final修饰的)。
代码演示:
/** 声明了一个接口:public interface 接口名{ // 常量;//抽象方法} 接口也是一种规范。规范一定是公开的。 */ public interface InterfaceDemo { //目标:理解接口中的成分特点:JDK 8 之前接口中只能有抽样方法和常量。 //1、常量 //注意:由于接口体现规范思想,规范默认一定是公开的,所有代码层面,public static final可以省略不写 String SHCOOL_NAME = "程序员"; //public static final String SHCOOL_NAME = "程序员"; //2、抽象方法 //注意:由于接口体现规范思想,规范默认一定是公开的,所有代码层面,public abstract可以省略不写 void run(); //public abstract void run(); void eat(); //public abstract void eat(); }
接口的基本使用:被实现
接口的用法:
- 接口是用来被类实现(implements)的,实现接口的类叫做实现类。实现类可以理解成所谓的子类。
接口可以被类单实现,也可以被类多实现!
修饰符 class 实现类 implements 接口1,接口2,接口3,...{} 实现的关键字:implements
代码演示:
运动员接口和规则接口:
/** 规范 */ /* 运动员 */ public interface SportMan { void run();//跑步 void conpetition();//比赛 }
public interface Law { void rule();//遵纪守法 }
乒乓球运动员(子类):
public class PingPongMan implements SportMan,Law {//多实现 private String name; public PingPongMan(String name) { this.name = name; } @Override public void run() { System.out.println(name + "必须跑步训练!"); } @Override public void conpetition() { System.out.println(name + "要参加比赛,为国争光--!"); } @Override public void rule() { System.out.println(name + "必须遵纪守法!"); } }
测试类:
public class Test { public static void main(String[] args) { //目标:理解接口的基本使用,被类实现。 //接口可以被类单实现,也可以被类多实现。 PingPongMan p = new PingPongMan("张继科"); p.run();//张继科必须跑步训练! p.conpetition();//张继科要参加比赛,为国争光--! p.rule();//张继科必须遵纪守法! } }
注意:
- 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类
接口与接口的关系:多继承
基本小结:
- 类和类的关系:单继承。
- 类和接口的关系:多实现。
- 接口与接口的关系:多继承,一个接口可以同时继承多个接口。
接口多继承的作用:
- 规范合并,整合多个接口为同一个接口,便于子类实现。
代码演示:
接口:运动员、人类、规则
让运动员接口去继承人类和规则接口。多继承
/** 接口可以多继承:一个接口可以同时继承多个接口。 */ public interface SportMan extends Law,People{ void run(); void conpetition(); }
public interface People { void eat(); void sleep(); }
public interface Law { void rule(); }
篮球运动员的实现类:
让运动员接口去继承人类和规则接口。实现多继承后,实现时,只需要实现运动员接口,避免代码冗余了。
/** 一个实现类 */ public class BasketBallMan implements SportMan{ @Override public void run() { } @Override public void conpetition() { } @Override public void rule() { } @Override public void eat() { } @Override public void sleep() { } }
jdk1.8后接口新增方法
Java对接口的成员方法进行了新增
第一种:默认方法
- 类似于之前写的普通实例方法:必须用default修饰
- 默认会public修饰。需要用接口的实现类的对象来调用。
default void run(){ System.out.println("--开始跑--"); }
第二种:静态方法
- 默认会public修饰。必须static修饰。
- 注意:接口的静态方法必须用本身的接口名来调用。
static void inAddr(){ System.out.println("我们都在快乐的学习java"); }
第三种:私有方法
- 就是私有的实例方法,必须使用private修饰,从jdk1.9才开始有的。
- 只能在本类种被其他的默认方法或者私有方法访问。
private void go(){ System.out.println("--准备--"); }
代码演示:
public interface SportManInter { /** 1、JDK 8开始:默认方法(实例方法) ---必须default修饰,默认用public修饰 ---默认方法。接口不能创建对象,这个方法只能过继给了实现类,由实现类对象调用 */ default void run(){ //go(); System.out.println("跑得很快---"); } /** 2、静态方法:必须使用static修饰,默认用public修饰 ----接口的静态方法,必须接口名自己调用。 */ static void inAddr(){ System.out.println("我们都在学习Java新增方法的语法,它是Java源码自己会用到的---"); } /** 3、私有方法:JDK1.9开始支持的(实例方法) 必须在接口内部才能被访问。private */ //private void go(){ //System.out.println("开始跑---"); //} } class PingPongMan implements SportManInter{ } class Test{ public static void main(String[] args) { PingPongMan b = new PingPongMan(); b.run(); SportManInter.inAddr(); //PingPongMan.inAddr(); } }
接口注意事项(了解)
- 接口不能创建对象(接口更加彻底的抽象)
- 一个类实现多个接口,多个接口中有同样的静态方法不冲突。
- 一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的
- 一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可
- 一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承
public static void main(String[] args) { //1、接口不能创建对象(接口更加彻底的抽象) //2、一个类实现多个接口,多个接口中有同样的静态方法不冲突。 Cat c = new Cat(); c.eat(); //3、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的 //4、一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可 //5、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承 } } //5、一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承 interface AAA{ void run(); } interface BBB{ void run(); } interface CCC extends AAA,BBB{ } //4、一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可 interface AA{ default void go(){ System.out.println("AA"); } } interface BB{ default void go(){ System.out.println("BB"); } } class CC implements AA ,BB{ @Override public void go() { } } //3、一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的 interface Food{ default void eat(){ System.out.println("接口中的吃方法--"); } } class Animal{ public void eat(){ System.out.println("父类动物吃--"); } } class Cat extends Animal implements Food,A{ } interface A{ public static void test(){ System.out.println("A"); } } interface B{ public static void test(){ System.out.println("B"); } } /** * //2、一个类实现多个接口,多个接口中有同样的静态方法不冲突。原因是接口的静态方法不能C调用,只能A调用A的,B调用B的 */ class C implements A,B{ }
接口和类的区别
类是有属性和方法组成.,接口也可以由属性和方法来组成
注意: 接口的属性必须是 public static final 的 (接口中成员变量必须是 公共的静态的常量
类的方法可以普通方法和抽象方法.
在 jdk1.8以前 接口中的方法都是抽象方法
但是在 jdk1.8以后. 新增的特性中允许 接口中也可以由默认(dedfaut)方法和静态方法(static)
类只能单继承. 但是 接口可以多继承.
接口不能被实例化的.
接口和抽象类的区别
抽象类可以有普通的属性, 但是接口只能是静态的常量.
抽象类可以有方法的实现 和抽象方法 ,但是接口中的方式都是必须抽象方法(jdk1.8以前)
注意: 在jdk1.8以后, 接口中可有默认方法(方法的实现)和静态方法.
接口可以解决java的单继承问题
java中解决多继承有两种方式:
内部类
接口
记住: 在java中 需要少继承,多实现.
注意: java中类只能进行单继承,但是 接口可以进行多继承
接口可以进行多继承 代码如下
类只能对单继承
何时使用抽象类? 何时使用接口?
与生俱来的属性和方法 放在抽象类中.
可扩展的功能功能放到接口中.