北京 网站建设托管公司,关键词快速排名怎么做,免费邮箱登录163登录,上海板块做企业优化的公司文章目录 第08章_面向对象编程(高级)本章专题与脉络1. 关键字#xff1a;static1.1 类属性、类方法的设计思想1.2 static关键字1.3 静态变量1.3.1 语法格式1.3.2 静态变量的特点1.3.3 举例1.3.4 内存解析 1.4 静态方法1.4.1 语法格式1.4.2 静态方法的特点1.4.3 举例 1.5 练习 … 文章目录 第08章_面向对象编程(高级)本章专题与脉络1. 关键字static1.1 类属性、类方法的设计思想1.2 static关键字1.3 静态变量1.3.1 语法格式1.3.2 静态变量的特点1.3.3 举例1.3.4 内存解析 1.4 静态方法1.4.1 语法格式1.4.2 静态方法的特点1.4.3 举例 1.5 练习 2. 单例(Singleton)设计模式2.1 设计模式概述2.2 何为单例模式2.3 实现思路2.4 单例模式的两种实现方式2.4.1 饿汉式2.4.2 懒汉式2.4.3 饿汉式 vs 懒汉式 2.5 单例模式的优点及应用场景 3. 理解main方法的语法4. 类的成员之四代码块4.1 静态代码块4.1.1 语法格式4.1.2 静态代码块的特点 4.2 非静态代码块4.2.1 语法格式4.2.2 非静态代码块的作用4.2.3 非静态代码块的意义4.2.4 非静态代码块的执行特点 4.3 举例4.4 小结实例变量赋值顺序4.5 练习 5. final关键字5.1 final的意义5.2 final的使用5.2.1 final修饰类5.2.2 final修饰方法5.2.3 final修饰变量 5.3 笔试题 6. 抽象类与抽象方法(或abstract关键字)6.1 由来6.2 语法格式6.3 使用说明6.4 注意事项6.5 应用举例16.6 应用举例2模板方法设计模式(TemplateMethod)6.7 思考与练习 7. 接口(interface)7.1 类比7.2 概述7.3 定义格式7.3.1 接口的声明格式7.3.2 接口的成员说明 7.4 接口的使用规则7.5 JDK8中相关冲突问题7.5.1 默认方法冲突问题7.5.2 常量冲突问题 7.6 接口的总结与面试题7.7 接口与抽象类之间的对比7.8 练习 8. 内部类InnerClass)8.1 概述8.1.1 什么是内部类8.1.2 为什么要声明内部类呢8.1.3 内部类的分类 8.2 成员内部类8.2.1 概述8.2.2 创建成员内部类对象8.2.3 举例 8.3 局部内部类8.3.1 非匿名局部内部类8.3.2 匿名内部类 8.4 练习 9. 枚举类9.1 概述9.2 定义枚举类JDK5.0 之前9.3 定义枚举类JDK5.0 之后9.3.1 enum关键字声明枚举9.3.2 enum方式定义的要求和特点 9.4 enum中常用方法9.5 实现接口的枚举类 10. 注解(Annotation)10.1 注解概述10.1.1 什么是注解10.1.2 注解与注释10.1.3 注解的重要性 10.2 常见的Annotation作用10.3 三个最基本的注解10.3.1 Override10.3.2 Deprecated10.3.3 SuppressWarnings 10.4 元注解10.5 自定义注解的使用10.5.1 声明自定义注解10.5.2 使用自定义注解10.5.3 读取和处理自定义注解 10.6 JUnit单元测试10.6.1 测试分类10.6.2 JUnit单元测试介绍10.6.3 引入本地JUnit.jar10.6.4 编写和运行Test单元测试方法10.6.5 设置执行JUnit用例时支持控制台输入10.6.6 定义test测试方法模板 11. 包装类11.1 为什么需要包装类11.2 有哪些包装类11.3 自定义包装类11.4 包装类与基本数据类型间的转换11.4.1 装箱11.4.2 拆箱 11.5 基本数据类型、包装类与字符串间的转换11.6 包装类的其它API11.6.1 数据类型的最大最小值11.6.2 字符转大小写11.6.3 整数转进制11.6.4 比较的方法 11.7 包装类对象的特点11.7.1 包装类缓存对象11.7.2 类型转换问题11.7.3 包装类对象不可变 11.8 练习 第08章_面向对象编程(高级) 本章专题与脉络 1. 关键字static
回顾类中的实例变量即非static的成员变量
class Circle{private double radius;public Circle(double radius){this.radiusradius;}public double findArea(){return Math.PI*radius*radius;}
}创建两个Circle对象
Circle c1new Circle(2.0); //c1.radius2.0
Circle c2new Circle(3.0); //c2.radius3.0Circle类中的变量radius是一个实例变量(instance variable)它属于类的每一个对象c1中的radius变化不会影响c2的radius反之亦然。
如果想让一个成员变量被类的所有实例所共享就用static修饰即可称为类变量或类属性
1.1 类属性、类方法的设计思想
当我们编写一个类时其实就是在描述其对象的属性和行为而并没有产生实质上的对象只有通过new关键字才会产出对象这时系统才会分配内存空间给对象其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下某些特定的数据在内存空间里只有一份。例如所有的中国人都有个国家名称每一个中国人都共享这个国家名称不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。 此外在类中声明的实例方法在类的外面必须要先创建对象才能调用。但是有些方法的调用者和当前类的对象无关这样的方法通常被声明为类方法由于不需要创建对象就可以调用类方法从而简化了方法的调用。
这里的类变量、类方法只需要使用static修饰即可。所以也称为静态变量、静态方法。
1.2 static关键字 使用范围 在Java类中可用static修饰属性、方法、代码块、内部类 被修饰后的成员具备以下特点 随着类的加载而加载优先于对象存在修饰的成员被所有对象所共享访问权限允许时可不创建对象直接被类调用
1.3 静态变量
1.3.1 语法格式
使用static修饰的成员变量就是静态变量或类变量、类属性
[修饰符] class 类{[其他修饰符] static 数据类型 变量名;
}1.3.2 静态变量的特点 静态变量的默认值规则和实例变量一样。 静态变量值是所有对象共享。 静态变量在本类中可以在任意方法、代码块、构造器中直接使用。 如果权限修饰符允许在其他类中可以通过“类名.静态变量”直接访问也可以通过“对象.静态变量”的方式访问但是更推荐使用类名.静态变量的方式。 静态变量的get/set方法也静态的当局部变量与静态变量重名时使用“类名.静态变量”进行区分。
1.3.3 举例
举例1
class Chinese{//实例变量String name;int age;//类变量static String nation;//国籍public Chinese() {}public Chinese(String name, int age) {this.name name;this.age age;}Overridepublic String toString() {return Chinese{ name name \ , age age , nation nation \ };}
}
public class StaticTest {public static void main(String[] args) {Chinese c1 new Chinese(康师傅,36);c1.nation 中华人民共和国;Chinese c2 new Chinese(老干妈,66);System.out.println(c1);System.out.println(c2);System.out.println(Chinese.nation);}
}对应的内存结构以经典的JDK6内存解析为例此时静态变量存储在方法区 举例2
package com.atguigu.keyword;public class Employee {private static int total;//这里私有化在类的外面必须使用get/set方法的方式来访问静态变量static String company; //这里缺省权限修饰符是为了方便类外以“类名.静态变量”的方式访问private int id;private String name;public Employee() {total;id total;//这里使用total静态变量的值为id属性赋值}public Employee(String name) {this();this.name name;}public void setId(int id) {this.id id;}public int getId() {return id;}public String getName() {return name;}public void setName(String name) {this.name name;}public static int getTotal() {return total;}public static void setTotal(int total) {Employee.total total;}Overridepublic String toString() {return Employee{company company ,id id ,name name };}
}package com.atguigu.keyword;public class TestStaticVariable {public static void main(String[] args) {//静态变量total的默认值是0System.out.println(Employee.total Employee.getTotal());Employee e1 new Employee(张三);Employee e2 new Employee(李四);System.out.println(e1);//静态变量company的默认值是nullSystem.out.println(e2);//静态变量company的默认值是nullSystem.out.println(Employee.total Employee.getTotal());//静态变量total值是2Employee.company 尚硅谷;System.out.println(e1);//静态变量company的值是尚硅谷System.out.println(e2);//静态变量company的值是尚硅谷//只要权限修饰符允许,虽然不推荐但是也可以通过“对象.静态变量”的形式来访问e1.company 超级尚硅谷;System.out.println(e1);//静态变量company的值是超级尚硅谷System.out.println(e2);//静态变量company的值是超级尚硅谷}
}1.3.4 内存解析 1.4 静态方法
1.4.1 语法格式
用static修饰的成员方法就是静态方法。
[修饰符] class 类{[其他修饰符] static 返回值类型 方法名(形参列表){方法体}
}1.4.2 静态方法的特点
静态方法在本类的任意方法、代码块、构造器中都可以直接被调用。只要权限修饰符允许静态方法在其他类中可以通过“类名.静态方法“的方式调用。也可以通过”对象.静态方法“的方式调用但是更推荐使用类名.静态方法的方式。在static方法内部只能访问类的static修饰的属性或方法不能访问类的非static的结构。静态方法可以被子类继承但不能被子类重写。静态方法的调用都只看编译时类型。因为不需要实例就可以访问static方法因此static方法内部不能有this也不能有super。如果有重名问题使用“类名.”进行区别。
1.4.3 举例
package com.atguigu.keyword;public class Father {public static void method(){System.out.println(Father.method);}public static void fun(){System.out.println(Father.fun);}
}package com.atguigu.keyword;public class Son extends Father{
// Override //尝试重写静态方法加上Override编译报错去掉Override不报错但是也不是重写public static void fun(){System.out.println(Son.fun);}
}package com.atguigu.keyword;public class TestStaticMethod {public static void main(String[] args) {Father.method();Son.method();//继承静态方法Father f new Son();f.method();//执行Father类中的method}
}1.5 练习
笔试题如下程序执行会不会报错
public class StaticTest {public static void main(String[] args) {Demo test null;test.hello();}
}class Demo{public static void hello(){System.out.println(hello!);}
}练习
编写一个类实现银行账户的概念包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”定义封装这些属性的方法。账号要自动生成。
编写主类使用银行账户类输入、输出3个储户的上述信息。
考虑哪些属性可以设计成static属性。
2. 单例(Singleton)设计模式
2.1 设计模式概述
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱不同的棋局我们用不同的棋谱。“套路”
经典的设计模式共有23种。每个设计模式均是特定环境下特定问题的处理方法。 简单工厂模式并不是23中经典模式的一种是其中工厂方法模式的简化版 对软件设计模式的研究造就了一本可能是面向对象设计方面最有影响的书籍《设计模式》《Design Patterns: Elements of Reusable Object-Oriented Software》即后述《设计模式》一书由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著Addison-Wesley1995。这几位作者常被称为四人组Gang of Four而这本书也就被称为四人组或 GoF书。 2.2 何为单例模式
所谓类的单例设计模式就是采取一定的方法保证在整个的软件系统中对某个类只能存在一个对象实例并且该类只提供一个取得其对象实例的方法。
2.3 实现思路
如果我们要让类在一个虚拟机中只能产生一个对象我们首先必须将类的构造器的访问权限设置为private这样就不能用new操作符在类的外部产生类的对象了但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象只能调用该类的某个静态方法以返回类内部创建的对象静态方法只能访问类中的静态成员变量所以指向类内部产生的该类对象的变量也必须定义成静态的。
2.4 单例模式的两种实现方式
2.4.1 饿汉式
class Singleton {// 1.私有化构造器private Singleton() {}// 2.内部提供一个当前类的实例// 4.此实例也必须静态化private static Singleton single new Singleton();// 3.提供公共的静态的方法返回当前类的对象public static Singleton getInstance() {return single;}
}
2.4.2 懒汉式
class Singleton {// 1.私有化构造器private Singleton() {}// 2.内部提供一个当前类的实例// 4.此实例也必须静态化private static Singleton single;// 3.提供公共的静态的方法返回当前类的对象public static Singleton getInstance() {if(single null) {single new Singleton();}return single;}
}
2.4.3 饿汉式 vs 懒汉式
饿汉式
特点立即加载即在使用类的时候已经将对象创建完毕。优点实现起来简单没有多线程安全问题。缺点当类被加载的时候会初始化static的实例静态变量被创建并分配内存空间从这以后这个static的实例便一直占着这块内存直到类被卸载时静态变量被摧毁并释放所占有的内存。因此在某些特定条件下会耗费内存。
懒汉式
特点延迟加载即在调用静态方法时实例才被创建。优点实现起来比较简单当类被加载的时候static的实例未被创建并分配内存空间当静态方法第一次被调用时初始化实例变量并分配内存因此在某些特定条件下会节约内存。缺点在多线程环境中这种实现方法是完全错误的线程不安全根本不能保证单例的唯一性。 说明在多线程章节会将懒汉式改造成线程安全的模式。
2.5 单例模式的优点及应用场景
由于单例模式只生成一个实例减少了系统性能开销当一个对象的产生需要比较多的资源时如读取配置、产生其他依赖对象时则可以通过在应用启动时直接产生一个单例对象然后永久驻留内存的方式来解决。
举例 应用场景 Windows的Task Manager (任务管理器)就是很典型的单例模式 Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中回收站一直维护着仅有的一个实例。 Application 也是单例的典型应用 应用程序的日志应用一般都使用单例模式实现这一般是由于共享的日志文件一直处于打开状态因为只 能有一个实例去操作否则内容不好追加。 数据库连接池的设计一般也是采用单例模式因为数据库连接是一种数据库资源。
3. 理解main方法的语法
由于JVM需要调用类的main()方法所以该方法的访问权限必须是public又因为JVM在执行main()方法时不必创建对象所以该方法必须是static的该方法接收一个String类型的数组参数该数组中保存执行Java命令时传递给所运行的类的参数。
又因为main() 方法是静态的我们不能直接访问该类中的非静态成员必须创建该类的一个实例对象后才能通过这个对象去访问类中的非静态成员这种情况我们在之前的例子中多次碰到。
命令行参数用法举例
public class CommandPara {public static void main(String[] args) {for (int i 0; i args.length; i) {System.out.println(args[ i ] args[i]);}}
}
//运行程序CommandPara.java
java CommandPara Tom Jerry Shkstart//输出结果
args[0] Tom
args[1] Jerry
args[2] Shkstart IDEA工具
1配置运行参数 2运行程序 笔试题
//此处Something类的文件名叫OtherThing.java
class Something {public static void main(String[] something_to_do) { System.out.println(Do something ...);}
}//上述程序是否可以正常编译、运行
4. 类的成员之四代码块
如果成员变量想要初始化的值不是一个硬编码的常量值而是需要通过复杂的计算或读取文件、或读取运行环境信息等方式才能获取的一些值该怎么办呢此时可以考虑代码块或初始化块。 代码块(或初始化块)的作用 对Java类或对象进行初始化 代码块(或初始化块)的分类 一个类中代码块若有修饰符则只能被static修饰称为静态代码块(static block) 没有使用static修饰的为非静态代码块。
4.1 静态代码块
如果想要为静态变量初始化可以直接在静态变量的声明后面直接赋值也可以使用静态代码块。
4.1.1 语法格式
在代码块的前面加static就是静态代码块。
【修饰符】 class 类{static{静态代码块}
}4.1.2 静态代码块的特点 可以有输出语句。 可以对类的属性、类的声明进行初始化操作。 不可以对非静态的属性初始化。即不可以调用非静态的属性和方法。 若有多个静态的代码块那么按照从上到下的顺序依次执行。 静态代码块的执行要先于非静态代码块。 静态代码块随着类的加载而加载且只执行一次。
package com.atguigu.keyword;public class Chinese {
// private static String country 中国;private static String country;private String name;{System.out.println(非静态代码块country country);}static {country 中国;System.out.println(静态代码块);}public Chinese(String name) {this.name name;}
}package com.atguigu.keyword;public class TestStaticBlock {public static void main(String[] args) {Chinese c1 new Chinese(张三);Chinese c2 new Chinese(李四);}
}
4.2 非静态代码块
4.2.1 语法格式
【修饰符】 class 类{{非静态代码块}【修饰符】 构造器名(){// 实例初始化代码}【修饰符】 构造器名(参数列表){// 实例初始化代码}
}4.2.2 非静态代码块的作用
和构造器一样也是用于实例变量的初始化等操作。
4.2.3 非静态代码块的意义
如果多个重载的构造器有公共代码并且这些代码都是先于构造器其他代码执行的那么可以将这部分代码抽取到非静态代码块中减少冗余代码。
4.2.4 非静态代码块的执行特点 可以有输出语句。 可以对类的属性、类的声明进行初始化操作。 除了调用非静态的结构外还可以调用静态的变量或方法。 若有多个非静态的代码块那么按照从上到下的顺序依次执行。 每次创建对象的时候都会执行一次。且先于构造器执行。
4.3 举例
举例1
1声明User类 包含属性usernameString类型passwordString类型registrationTimelong类型私有化 包含get/set方法其中registrationTime没有set方法 包含无参构造 输出“新用户注册”registrationTime赋值为当前系统时间username就默认为当前系统时间值password默认为“123456” 包含有参构造(String username, String password) 输出“新用户注册”registrationTime赋值为当前系统时间username和password由参数赋值 包含public String getInfo()方法返回“用户名xx密码xx注册时间xx”
2编写测试类测试类main方法的代码如下 public static void main(String[] args) {User u1 new User();System.out.println(u1.getInfo());User u2 new User(song,8888);System.out.println(u2.getInfo());}如果不用非静态代码块User类是这样的
package com.atguigu.block.no;public class User {private String username;private String password;private long registrationTime;public User() {System.out.println(新用户注册);registrationTime System.currentTimeMillis();username registrationTime;password 123456;}public User(String username,String password) {System.out.println(新用户注册);registrationTime System.currentTimeMillis();this.username username;this.password password;}public String getUsername() {return username;}public void setUsername(String username) {this.username username;}public String getPassword() {return password;}public void setPassword(String password) {this.password password;}public long getRegistrationTime() {return registrationTime;}public String getInfo(){return 用户名 username 密码 password 注册时间 registrationTime;}
}如果提取构造器公共代码到非静态代码块User类是这样的
package com.atguigu.block.use;public class User {private String username;private String password;private long registrationTime;{System.out.println(新用户注册);registrationTime System.currentTimeMillis();}public User() {username registrationTime;password 123456;}public User(String username, String password) {this.username username;this.password password;}public String getUsername() {return username;}public void setUsername(String username) {this.username username;}public String getPassword() {return password;}public void setPassword(String password) {this.password password;}public long getRegistrationTime() {return registrationTime;}public String getInfo(){return 用户名 username 密码 password 注册时间 registrationTime;}
}举例2
private static DataSource dataSource null;static{InputStream is null;try {is DBCPTest.class.getClassLoader().getResourceAsStream(dbcp.properties);Properties pros new Properties();pros.load(is);//调用BasicDataSourceFactory的静态方法获取数据源。dataSource BasicDataSourceFactory.createDataSource(pros);} catch (Exception e) {e.printStackTrace();}finally{if(is ! null){try {is.close();} catch (IOException e) {e.printStackTrace();} } }
}4.4 小结实例变量赋值顺序 4.5 练习
练习1分析加载顺序
class Root{static{System.out.println(Root的静态初始化块);}{System.out.println(Root的普通初始化块);}public Root(){System.out.println(Root的无参数的构造器);}
}
class Mid extends Root{static{System.out.println(Mid的静态初始化块);}{System.out.println(Mid的普通初始化块);}public Mid(){System.out.println(Mid的无参数的构造器);}public Mid(String msg){//通过this调用同一类中重载的构造器this();System.out.println(Mid的带参数构造器其参数值 msg);}
}
class Leaf extends Mid{static{System.out.println(Leaf的静态初始化块);}{System.out.println(Leaf的普通初始化块);} public Leaf(){//通过super调用父类中有一个字符串参数的构造器super(尚硅谷);System.out.println(Leaf的构造器);}
}
public class LeafTest{public static void main(String[] args){new Leaf(); //new Leaf();}
}练习2分析加载顺序
class Father {static {System.out.println(11111111111);}{System.out.println(22222222222);}public Father() {System.out.println(33333333333);}}public class Son extends Father {static {System.out.println(44444444444);}{System.out.println(55555555555);}public Son() {System.out.println(66666666666);}public static void main(String[] args) { System.out.println(77777777777);System.out.println(************************);new Son();System.out.println(************************);new Son();System.out.println(************************);new Father();}}
练习3
package com.atguigu05.field.interview;
public class Test04 {public static void main(String[] args) {Zi zi new Zi();}
}
class Fu{private static int i getNum(1i);private int j getNum(2j);static{print(3父类静态代码块);}{print(4父类非静态代码块又称为构造代码块);}Fu(){print(5父类构造器);}public static void print(String str){System.out.println(str - i);}public static int getNum(String str){print(str);return i;}
}
class Zi extends Fu{private static int k getNum(6k);private int h getNum(7h);static{print(8子类静态代码块);}{print(9子类非静态代码块又称为构造代码块);}Zi(){print(10子类构造器);}public static void print(String str){System.out.println(str - k);}public static int getNum(String str){print(str);return k;}
}
5. final关键字
5.1 final的意义
final最终的不可更改的
5.2 final的使用
5.2.1 final修饰类
表示这个类不能被继承没有子类。提高安全性提高程序的可读性。
例如String类、System类、StringBuffer类
final class Eunuch{//太监类}
class Son extends Eunuch{//错误}5.2.2 final修饰方法
表示这个方法不能被子类重写。
例如Object类中的getClass()
class Father{public final void method(){System.out.println(father);}
}
class Son extends Father{public void method(){//错误System.out.println(son);}
}5.2.3 final修饰变量
final修饰某个变量成员变量或局部变量一旦赋值它的值就不能被修改即常量常量名建议使用大写字母。
例如final double MY_PI 3.14; 如果某个成员变量用final修饰后没有set方法并且必须初始化可以显式赋值、或在初始化块赋值、实例变量还可以在构造器中赋值 修饰成员变量
public final class Test {public static int totalNumber 5;public final int ID;public Test() {ID totalNumber; // 可在构造器中给final修饰的“变量”赋值}public static void main(String[] args) {Test t new Test();System.out.println(t.ID);}
}
修饰局部变量
public class TestFinal {public static void main(String[] args){final int MIN_SCORE ;MIN_SCORE 0;final int MAX_SCORE 100;MAX_SCORE 200; //非法}
}错误演示
class A {private final String INFO atguigu; //声明常量public void print() {//The final field A.INFO cannot be assigned//INFO 尚硅谷; }
}
5.3 笔试题
题1排错
public class Something {public int addOne(final int x) {return x;// return x 1;}
}题2排错
public class Something {public static void main(String[] args) {Other o new Other();new Something().addOne(o);}public void addOne(final Other o) {// o new Other();o.i;}
}
class Other {public int i;
}
6. 抽象类与抽象方法(或abstract关键字)
6.1 由来
举例1
随着继承层次中一个个新子类的定义类变得越来越具体而父类则更一般更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象以至于它没有具体的实例这样的类叫做抽象类。 举例2
我们声明一些几何图形类圆、矩形、三角形类等发现这些类都有共同特征求面积、求周长。那么这些共同特征应该抽取到一个共同父类几何图形类中。但是这些方法在父类中又无法给出具体的实现而是应该交给子类各自具体实现。那么父类在声明这些方法时就只有方法签名没有方法体我们把没有方法体的方法称为抽象方法。Java语法规定包含抽象方法的类必须是抽象类。
6.2 语法格式
抽象类被abstract修饰的类。抽象方法被abstract修饰没有方法体的方法。
抽象类的语法格式
[权限修饰符] abstract class 类名{}
[权限修饰符] abstract class 类名 extends 父类{}抽象方法的语法格式
[其他修饰符] abstract 返回值类型 方法名([形参列表]);注意抽象方法没有方法体 代码举例
public abstract class Animal {public abstract void eat();
}public class Cat extends Animal {public void eat (){System.out.println(小猫吃鱼和猫粮); }
}public class CatTest {public static void main(String[] args) {// 创建子类对象Cat c new Cat(); // 调用eat方法c.eat();}
}此时的方法重写是子类对父类抽象方法的完成实现我们将这种方法重写的操作也叫做实现方法。
6.3 使用说明 抽象类不能创建对象如果创建编译无法通过而报错。只能创建其非抽象子类的对象。 理解假设创建了抽象类的对象调用抽象的方法而抽象方法没有具体的方法体没有意义。 抽象类是用来被继承的抽象类的子类必须重写父类的抽象方法并提供方法体。若没有重写全部的抽象方法仍为抽象类。 抽象类中也有构造方法是供子类创建对象时初始化父类成员变量使用的。 理解子类的构造方法中有默认的super()或手动的super(实参列表)需要访问父类构造方法。 抽象类中不一定包含抽象方法但是有抽象方法的类必定是抽象类。 理解未包含抽象方法的抽象类目的就是不想让调用者创建该类对象通常用于某些特殊的类结构设计。 抽象类的子类必须重写抽象父类中所有的抽象方法否则编译无法通过而报错。除非该子类也是抽象类。 理解假设不重写所有抽象方法则类中可能包含抽象方法。那么创建对象后调用抽象的方法没有意义。
6.4 注意事项 不能用abstract修饰变量、代码块、构造器 不能用abstract修饰私有方法、静态方法、final的方法、final的类。
6.5 应用举例1 在航运公司系统中Vehicle类需要定义两个方法分别计算运输工具的燃料效率和行驶距离。
**问题**卡车(Truck)和驳船(RiverBarge)的燃料效率和行驶距离的计算方法完全不同。Vehicle类不能提供计算方法但子类可以。
**解决方案**Java允许类设计者指定超类声明一个方法但不提供实现该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。
//Vehicle是一个抽象类有两个抽象方法。
public abstract class Vehicle{public abstract double calcFuelEfficiency(); //计算燃料效率的抽象方法public abstract double calcTripDistance(); //计算行驶距离的抽象方法
}
public class Truck extends Vehicle{public double calcFuelEfficiency( ) { //写出计算卡车的燃料效率的具体方法 }public double calcTripDistance( ) { //写出计算卡车行驶距离的具体方法 }
}
public class RiverBarge extends Vehicle{public double calcFuelEfficiency( ) { //写出计算驳船的燃料效率的具体方法 }public double calcTripDistance( ) { //写出计算驳船行驶距离的具体方法}
}
6.6 应用举例2模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计抽象类作为多个子类的通用模板子类在抽象类的基础上进行扩展、改造但子类总体上会保留抽象类的行为方式。
解决的问题 当功能内部一部分实现是确定的另一部分实现是不确定的。这时可以把不确定的部分暴露出去让子类去实现。 换句话说在软件开发中实现一个算法时整体步骤很固定、通用这些步骤已经在父类中写好了。但是某些部分易变易变部分可以抽象出来供不同子类实现。这就是一种模板模式。
**类比举例**英语六级模板 制作月饼的模板 举例1
abstract class Template {public final void getTime() {long start System.currentTimeMillis();code();long end System.currentTimeMillis();System.out.println(执行时间是 (end - start));}public abstract void code();
}class SubTemplate extends Template {public void code() {for (int i 0; i 10000; i) {System.out.println(i);}}
}
举例2
package com.atguigu.java;
//抽象类的应用模板方法的设计模式
public class TemplateMethodTest {public static void main(String[] args) {BankTemplateMethod btm new DrawMoney();btm.process();BankTemplateMethod btm2 new ManageMoney();btm2.process();}
}
abstract class BankTemplateMethod {// 具体方法public void takeNumber() {System.out.println(取号排队);}public abstract void transact(); // 办理具体的业务 //钩子方法public void evaluate() {System.out.println(反馈评分);}// 模板方法把基本操作组合到一起子类一般不能重写public final void process() {this.takeNumber();this.transact();// 像个钩子具体执行时挂哪个子类就执行哪个子类的实现代码this.evaluate();}
}class DrawMoney extends BankTemplateMethod {public void transact() {System.out.println(我要取款);}
}class ManageMoney extends BankTemplateMethod {public void transact() {System.out.println(我要理财我这里有2000万美元!!);}
}
模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子比如常见的有 数据库访问的封装 Junit单元测试 JavaWeb的Servlet中关于doGet/doPost方法调用 Hibernate中模板程序 Spring中JDBCTemlate、HibernateTemplate等
6.7 思考与练习
思考
问题1为什么抽象类不可以使用final关键字声明
问题2一个抽象类中可以定义构造器吗
问题3是否可以这样理解抽象类就是比普通类多定义了抽象方法除了不能直接进行类的实例化操作之外并没有任何的不同
练习1
编写一个Employee类声明为抽象类包含如下三个属性nameidsalary。提供必要的构造器和抽象方法work()。
对于Manager类来说他既是员工还具有奖金(bonus)的属性。
请使用继承的思想设计CommonEmployee类和Manager类要求类中提供必要的方法进行属性访问。
练习2软件外包公司外派管理
有一家软件外包公司可以外派开发人员该公司有两个角色普通开发人员Developer和项目经理Manager。他们的关系如下图 普通开发人员的工作内容是“开发项目”项目经理的工作内容是“项目管理”。对外的报价是普通开发人员每天500,元超过60天每天400元。项目经理每天800元超过60天每天700元。
有一家银行需要1名项目经理、2名开发人员现场开发90天计算银行需要付给软件公司的总金额。
提示创建数组 Employee[] emps new Employee[3]。其中存储驻场的3名员工。
练习3
创建父类Shape包含绘制形状的抽象方法draw()。
创建Shape的子类Circle和Rectangle重写draw()方法绘制圆形和矩形。
绘制多个圆形和矩形。
练习4
1、声明抽象父类Person包含抽象方法public abstract void eat(); 2、声明子类中国人Chinese重写抽象方法打印用筷子吃饭 3、声明子类美国人American重写抽象方法打印用刀叉吃饭 4、声明子类印度人Indian重写抽象方法打印用手抓饭 5、声明测试类PersonTest创建Person数组存储各国人对象并遍历数组调用eat()方法
练习5工资系统设计
编写工资系统实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日则将该雇员的工资增加100元。
实验说明
1定义一个Employee类该类包含
private成员变量name,number,birthday其中birthday 为MyDate类的对象
abstract方法earnings()
toString()方法输出对象的name,number和birthday。
2MyDate类包含:
private成员变量year,month,day
toDateString()方法返回日期对应的字符串xxxx年xx月xx日
3定义SalariedEmployee类继承Employee类实现按月计算工资的员工处理。该类包括private成员变量monthlySalary
实现父类的抽象方法earnings(),该方法返回monthlySalary值toString()方法输出员工类型信息及员工的namenumber,birthday。
4参照SalariedEmployee类定义HourlyEmployee类实现按小时计算工资的员工处理。该类包括
private成员变量wage和hour
实现父类的抽象方法earnings(),该方法返回wage*hour值
toString()方法输出员工类型信息及员工的namenumber,birthday。
5定义PayrollSystem类创建Employee变量数组并初始化该数组存放各类雇员对象的引用。利用循环结构遍历数组元素输出各个对象的类型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时如果本月是某个Employee对象的生日还要输出增加工资信息。
//提示
//定义People类型的数组People c1[]new People[10];
//数组元素赋值
c1[0]new People(John,0001,20);
c1[1]new People(Bob,0002,19);
//若People有两个子类Student和Officer则数组元素赋值时可以使父类类型的数组元素指向子类。
c1[0]new Student(John,0001,20,85.0);
c1[1]new Officer(Bob,0002,19,90.5);
7. 接口(interface)
7.1 类比
生活中大家每天都在用USB接口那么USB接口与我们今天要学习的接口有什么相同点呢
USBUniversal Serial Bus通用串行总线是Intel公司开发的总线架构使得在计算机上添加串行设备鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等非常容易。其实不管是电脑上的USB插口还是其他设备上的USB插口都只是遵循了USB规范的一种具体设备而已。 只要设备遵循USB规范的那么就可以与电脑互联并正常通信。至于这个设备、电脑是哪个厂家制造的内部是如何实现的我们都无需关心。
Java的软件系统会有很多模块组成那么各个模块之间也应该采用这种面向接口的低耦合为系统提供更好的可扩展性和可维护性。
7.2 概述
接口就是规范定义的是一组规则体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个是不是的is-a关系而接口实现则是 能不能的has-a关系。
例如电脑都预留了可以插入USB设备的USB接口USB接口具备基本的数据传输的开启功能和关闭功能。你能不能用USB进行连接或是否具备USB通信功能就看你能否遵循USB接口规范 例如Java程序是否能够连接使用某种数据库产品那么要看该数据库产品能否实现Java设计的JDBC规范 接口的本质是契约、标准、规范就像我们的法律一样。制定好后大家都要遵守。 7.3 定义格式
接口的定义它与定义类方式相似但是使用 interface 关键字。它也会被编译成.class文件但一定要明确它并不是类而是另外一种引用数据类型。 引用数据类型数组类枚举接口注解。 7.3.1 接口的声明格式
[修饰符] interface 接口名{//接口的成员列表// 公共的静态常量// 公共的抽象方法// 公共的默认方法JDK1.8以上// 公共的静态方法JDK1.8以上// 私有方法JDK1.9以上
}示例代码
package com.atguigu.interfacetype;public interface USB3{//静态常量long MAX_SPEED 500*1024*1024;//500MB/s//抽象方法void in();void out();//默认方法default void start(){System.out.println(开始);}default void stop(){System.out.println(结束);}//静态方法static void show(){System.out.println(USB 3.0可以同步全速地进行读写操作);}
}7.3.2 接口的成员说明
在JDK8.0 之前接口中只允许出现
1公共的静态的常量其中public static final可以省略
2公共的抽象的方法其中public abstract可以省略 理解接口是从多个相似类中抽象出来的规范不需要提供具体实现 在JDK8.0 时接口中允许声明默认方法和静态方法
3公共的默认的方法其中public 可以省略建议保留但是default不能省略
4公共的静态的方法其中public 可以省略建议保留但是static不能省略
在JDK9.0 时接口又增加了
5私有方法
除此之外接口中没有构造器没有初始化块因为接口中没有成员变量需要动态初始化。
7.4 接口的使用规则
1、类实现接口implements
接口不能创建对象但是可以被类实现implements 类似于被继承。
类与接口的关系为实现关系即类实现接口该类可以称为接口的实现类。实现的动作类似继承格式相仿只是关键字不同实现使用 implements关键字。
【修饰符】 class 实现类 implements 接口{// 重写接口中抽象方法【必须】当然如果实现类是抽象类那么可以不重写// 重写接口中默认方法【可选】
}【修饰符】 class 实现类 extends 父类 implements 接口{// 重写接口中抽象方法【必须】当然如果实现类是抽象类那么可以不重写// 重写接口中默认方法【可选】
}注意 如果接口的实现类是非抽象类那么必须重写接口中所有抽象方法。 默认方法可以选择保留也可以重写。 重写时default单词就不要再写了它只用于在接口中表示默认方法到类中就没有默认方法的概念了 接口中的静态方法不能被继承也不能被重写
举例
interface USB{ // public void start() ;public void stop() ;
}
class Computer{public static void show(USB usb){ usb.start() ;System.out.println( USB 设备工作 ) ;usb.stop() ;}
};
class Flash implements USB{public void start(){ // 重写方法System.out.println(U盘开始工作。) ;}public void stop(){ // 重写方法System.out.println(U盘停止工作。) ;}
};
class Print implements USB{public void start(){ // 重写方法System.out.println(打印机开始工作。) ;}public void stop(){ // 重写方法System.out.println(打印机停止工作。) ;}
};
public class InterfaceDemo{public static void main(String args[]){Computer.show(new Flash()) ;Computer.show(new Print()) ;c.show(new USB(){public void start(){System.out.println(移动硬盘开始运行);}public void stop(){System.out.println(移动硬盘停止运行);}});}
};2、接口的多实现implements
之前学过在继承体系中一个类只能继承一个父类。而对于接口而言一个类是可以实现多个接口的这叫做接口的多实现。并且一个类能继承一个父类同时实现多个接口。
实现格式
【修饰符】 class 实现类 implements 接口1接口2接口3。。。{// 重写接口中所有抽象方法【必须】当然如果实现类是抽象类那么可以不重写// 重写接口中默认方法【可选】
}【修饰符】 class 实现类 extends 父类 implements 接口1接口2接口3。。。{// 重写接口中所有抽象方法【必须】当然如果实现类是抽象类那么可以不重写// 重写接口中默认方法【可选】
}接口中有多个抽象方法时实现类必须重写所有抽象方法。如果抽象方法有重名的只需要重写一次。 举例 定义多个接口
package com.atguigu.interfacetype;public interface A {void showA();
}package com.atguigu.interfacetype;public interface B {void showB();
}定义实现类
package com.atguigu.interfacetype;public class C implements A,B {Overridepublic void showA() {System.out.println(showA);}Overridepublic void showB() {System.out.println(showB);}
}
测试类
package com.atguigu.interfacetype;public class TestC {public static void main(String[] args) {C c new C();c.showA();c.showB();}
}3、接口的多继承(extends)
一个接口能继承另一个或者多个接口接口的继承也使用 extends 关键字子接口继承父接口的方法。
定义父接口
package com.atguigu.interfacetype;public interface Chargeable {void charge();void in();void out();
}定义子接口
package com.atguigu.interfacetype;public interface UsbC extends Chargeable,USB3 {void reverse();
}定义子接口的实现类
package com.atguigu.interfacetype;public class TypeCConverter implements UsbC {Overridepublic void reverse() {System.out.println(正反面都支持);}Overridepublic void charge() {System.out.println(可充电);}Overridepublic void in() {System.out.println(接收数据);}Overridepublic void out() {System.out.println(输出数据);}
}所有父接口的抽象方法都有重写。 方法签名相同的抽象方法只需要实现一次。 4、接口与实现类对象构成多态引用
实现类实现接口类似于子类继承父类因此接口类型的变量与实现类的对象之间也可以构成多态引用。通过接口类型的变量调用方法最终执行的是你new的实现类对象实现的方法体。
接口的不同实现类
package com.atguigu.interfacetype;public class Mouse implements USB3 {Overridepublic void out() {System.out.println(发送脉冲信号);}Overridepublic void in() {System.out.println(不接收信号);}
}package com.atguigu.interfacetype;public class KeyBoard implements USB3{Overridepublic void in() {System.out.println(不接收信号);}Overridepublic void out() {System.out.println(发送按键信号);}
}
测试类
package com.atguigu.interfacetype;public class TestComputer {public static void main(String[] args) {Computer computer new Computer();USB3 usb new Mouse();computer.setUsb(usb);usb.start();usb.out();usb.in();usb.stop();System.out.println(--------------------------);usb new KeyBoard();computer.setUsb(usb);usb.start();usb.out();usb.in();usb.stop();System.out.println(--------------------------);usb new MobileHDD();computer.setUsb(usb);usb.start();usb.out();usb.in();usb.stop();}
}5、使用接口的静态成员
接口不能直接创建对象但是可以通过接口名直接调用接口的静态方法和静态常量。
package com.atguigu.interfacetype;public class TestUSB3 {public static void main(String[] args) {//通过“接口名.”调用接口的静态方法 (JDK8.0才能开始使用)USB3.show();//通过“接口名.”直接使用接口的静态常量System.out.println(USB3.MAX_SPEED);}
}6、使用接口的非静态方法
对于接口的静态方法直接使用“接口名.”进行调用即可 也只能使用“接口名.进行调用不能通过实现类的对象进行调用 对于接口的抽象方法、默认方法只能通过实现类对象才可以调用 接口不能直接创建对象只能创建实现类的对象
package com.atguigu.interfacetype;public class TestMobileHDD {public static void main(String[] args) {//创建实现类对象MobileHDD b new MobileHDD();//通过实现类对象调用重写的抽象方法以及接口的默认方法如果实现类重写了就执行重写的默认方法如果没有重写就执行接口中的默认方法b.start();b.in();b.stop();//通过接口名调用接口的静态方法
// MobileHDD.show();
// b.show();Usb3.show();}
}7.5 JDK8中相关冲突问题
7.5.1 默认方法冲突问题
1类优先原则
当一个类既继承一个父类又实现若干个接口时父类中的成员方法与接口中的抽象方法重名子类就近选择执行父类的成员方法。代码如下
定义接口
package com.atguigu.interfacetype;public interface Friend {default void date(){//约会System.out.println(吃喝玩乐);}
}定义父类
package com.atguigu.interfacetype;public class Father {public void date(){//约会System.out.println(爸爸约吃饭);}
}定义子类
package com.atguigu.interfacetype;public class Son extends Father implements Friend {Overridepublic void date() {//(1)不重写默认保留父类的//(2)调用父类被重写的
// super.date();//(3)保留父接口的
// Friend.super.date();//(4)完全重写System.out.println(跟康师傅学Java);}
}定义测试类
package com.atguigu.interfacetype;public class TestSon {public static void main(String[] args) {Son s new Son();s.date();}
}2接口冲突左右为难
当一个类同时实现了多个父接口而多个父接口中包含方法签名相同的默认方法时怎么办呢 无论你多难抉择最终都是要做出选择的。
声明接口
package com.atguigu.interfacetype;public interface BoyFriend {default void date(){//约会System.out.println(神秘约会);}
}选择保留其中一个通过“接口名.super.方法名的方法选择保留哪个接口的默认方法。
package com.atguigu.interfacetype;public class Girl implements Friend,BoyFriend{Overridepublic void date() {//(1)保留其中一个父接口的
// Friend.super.date();
// BoyFriend.super.date();//(2)完全重写System.out.println(跟康师傅学Java);}}测试类
package com.atguigu.interfacetype;public class TestGirl {public static void main(String[] args) {Girl girl new Girl();girl.date();}
}当一个子接口同时继承了多个接口而多个父接口中包含方法签名相同的默认方法时怎么办呢
另一个父接口
package com.atguigu.interfacetype;public interface USB2 {//静态常量long MAX_SPEED 60*1024*1024;//60MB/s//抽象方法void in();void out();//默认方法public default void start(){System.out.println(开始);}public default void stop(){System.out.println(结束);}//静态方法public static void show(){System.out.println(USB 2.0可以高速地进行读写操作);}
}子接口
package com.atguigu.interfacetype;public interface USB extends USB2,USB3 {Overridedefault void start() {System.out.println(Usb.start);}Overridedefault void stop() {System.out.println(Usb.stop);}
} 小贴士 子接口重写默认方法时default关键字可以保留。 子类重写默认方法时default关键字不可以保留。 7.5.2 常量冲突问题
当子类继承父类又实现父接口而父类中存在与父接口常量同名的成员变量并且该成员变量名在子类中仍然可见。当子类同时实现多个接口而多个接口存在相同同名常量。
此时在子类中想要引用父类或父接口的同名的常量或成员变量时就会有冲突问题。
父类和父接口
package com.atguigu.interfacetype;public class SuperClass {int x 1;
}package com.atguigu.interfacetype;public interface SuperInterface {int x 2;int y 2;
}package com.atguigu.interfacetype;public interface MotherInterface {int x 3;
}子类
package com.atguigu.interfacetype;public class SubClass extends SuperClass implements SuperInterface,MotherInterface {public void method(){
// System.out.println(x x);//模糊不清System.out.println(super.x super.x);System.out.println(SuperInterface.x SuperInterface.x);System.out.println(MotherInterface.x MotherInterface.x);System.out.println(y y);//没有重名问题可以直接访问}
}7.6 接口的总结与面试题
接口本身不能创建对象只能创建接口的实现类对象接口类型的变量可以与实现类对象构成多态引用。声明接口用interface接口的成员声明有限制 1公共的静态常量2公共的抽象方法3公共的默认方法JDK8.0 及以上4公共的静态方法JDK8.0 及以上5私有方法JDK9.0 及以上 类可以实现接口关键字是implements而且支持多实现。如果实现类不是抽象类就必须实现接口中所有的抽象方法。如果实现类既要继承父类又要实现父接口那么继承extends在前实现implements在后。接口可以继承接口关键字是extends而且支持多继承。接口的默认方法可以选择重写或不重写。如果有冲突问题另行处理。子类重写父接口的默认方法要去掉default子接口重写父接口的默认方法不要去掉default。接口的静态方法不能被继承也不能被重写。接口的静态方法只能通过“接口名.静态方法名”进行调用。
面试题
1、为什么接口中只能声明公共的静态的常量
因为接口是标准规范那么在规范中需要声明一些底线边界值当实现者在实现这些规范时不能去随意修改和触碰这些底线否则就有“危险”。
例如USB1.0规范中规定最大传输速率是1.5Mbps最大输出电流是5V/500mA
USB3.0规范中规定最大传输速率是5Gbps(500MB/s)最大输出电流是5V/900mA
例如尚硅谷学生行为规范中规定学员早上8:25之前进班晚上21:30之后离开等等。
2、为什么JDK8.0 之后允许接口定义静态方法和默认方法呢因为它违反了接口作为一个抽象标准定义的概念。
静态方法因为之前的标准类库设计中有很多Collection/Colletions或者Path/Paths这样成对的接口和类后面的类中都是静态方法而这些静态方法都是为前面的接口服务的那么这样设计一对API不如把静态方法直接定义到接口中使用和维护更方便。
默认方法1我们要在已有的老版接口中提供新方法时如果添加抽象方法就会涉及到原来使用这些接口的类就会有问题那么为了保持与旧版本代码的兼容性只能允许在接口中定义默认方法实现。比如Java8中对Collection、List、Comparator等接口提供了丰富的默认方法。2当我们接口的某个抽象方法在很多实现类中的实现代码是一样的此时将这个抽象方法设计为默认方法更为合适那么实现类就可以选择重写也可以选择不重写。
3、为什么JDK1.9要允许接口定义私有方法呢因为我们说接口是规范规范是需要公开让大家遵守的。
私有方法因为有了默认方法和静态方法这样具有具体实现的方法那么就可能出现多个方法由共同的代码可以抽取而这些共同的代码抽取出来的方法又只希望在接口内部使用所以就增加了私有方法。
7.7 接口与抽象类之间的对比 在开发中常看到一个类不是去继承一个已经实现好的类而是要么继承抽象类要么实现接口。 7.8 练习
**笔试题**排错
interface A {int x 0;
}
class B {int x 1;
}
class C extends B implements A {public void pX() {System.out.println(x);}public static void main(String[] args) {new C().pX();}
}
**笔试题**排错
interface Playable {void play();
}interface Bounceable {void play();
}interface Rollable extends Playable, Bounceable {Ball ball new Ball(PingPang);}class Ball implements Rollable {private String name;public String getName() {return name;}public Ball(String name) {this.name name;}public void play() {ball new Ball(Football);System.out.println(ball.getName());}
}
练习1
定义一个接口用来实现两个对象的比较。
interface CompareObject{//若返回值是 0 , 代表相等; 若为正数代表当前对象大负数代表当前对象小public int compareTo(Object o);
}定义一个Circle类声明redius属性提供getter和setter方法
定义一个ComparableCircle类继承Circle类并且实现CompareObject接口。在ComparableCircle类中给出接口中方法compareTo的实现体用来比较两个圆的半径大小。
定义一个测试类InterfaceTest创建两个ComparableCircle对象调用compareTo方法比较两个类的半径大小。
思考参照上述做法定义矩形类Rectangle和ComparableRectangle类在ComparableRectangle类中给出compareTo方法的实现比较两个矩形的面积大小。
练习2交通工具案例
阿里的一个工程师声明的属性和方法如下 其中有一个乘坐交通工具的方法takingVehicle()在此方法中调用交通工具的run()。为了出行方便他买了一辆捷安特自行车、一辆雅迪电动车和一辆奔驰轿车。这里涉及到的相关类及接口关系如下 其中电动车增加动力的方式是充电轿车增加动力的方式是加油。在具体交通工具的run()中调用其所在类的相关属性信息。
请编写相关代码并测试。
提示创建Vehicle[]数组保存阿里工程师的三辆交通工具并分别在工程师的takingVehicle()中调用。
8. 内部类InnerClass)
8.1 概述
8.1.1 什么是内部类
将一个类A定义在另一个类B里面里面的那个类A就称为内部类InnerClass类B则称为外部类OuterClass。
8.1.2 为什么要声明内部类呢
具体来说当一个事物A的内部还有一个部分需要一个完整的结构B进行描述而这个内部的完整的结构B又只为外部事物A提供服务不在其他地方单独使用那么整个内部的完整结构B最好使用内部类。
总的来说遵循高内聚、低耦合的面向对象开发原则。
8.1.3 内部类的分类
根据内部类声明的位置如同变量的分类我们可以分为 8.2 成员内部类
8.2.1 概述
如果成员内部类中不使用外部类的非静态成员那么通常将内部类声明为静态内部类否则声明为非静态内部类。
语法格式
[修饰符] class 外部类{[其他修饰符] [static] class 内部类{}
}成员内部类的使用特征概括来讲有如下两种角色
成员内部类作为类的成员的角色 和外部类不同Inner class还可以声明为private或protected可以调用外部类的结构。注意在静态内部类中不能使用外部类的非静态成员Inner class 可以声明为static的但此时就不能再使用外层类的非static的成员变量 成员内部类作为类的角色 可以在内部定义属性、方法、构造器等结构可以继承自己的想要继承的父类实现自己想要实现的父接口们和外部类的父类和父接口无关可以声明为abstract类 因此可以被其它的内部类继承可以声明为final的表示不能被继承编译以后生成OuterClass$InnerClass.class字节码文件也适用于局部内部类
注意点 外部类访问成员内部类的成员需要“内部类.成员”或“内部类对象.成员”的方式 成员内部类可以直接使用外部类的所有成员包括私有的数据 当想要在外部类的静态成员部分使用内部类时可以考虑内部类声明为静态的
8.2.2 创建成员内部类对象
实例化静态内部类
外部类名.静态内部类名 变量 外部类名.静态内部类名();
变量.非静态方法();实例化非静态内部类
外部类名 变量1 new 外部类();
外部类名.非静态内部类名 变量2 变量1.new 非静态内部类名();
变量2.非静态方法();8.2.3 举例
public class TestMemberInnerClass {public static void main(String[] args) {//创建静态内部类实例并调用方法Outer.StaticInner inner new Outer.StaticInner();inner.inFun();//调用静态内部类静态方法Outer.StaticInner.inMethod();System.out.println(*****************************);//创建非静态内部类实例方式1并调用方法Outer outer new Outer();Outer.NoStaticInner inner1 outer.new NoStaticInner();inner1.inFun();//创建非静态内部类实例方式2Outer.NoStaticInner inner2 outer.getNoStaticInner();inner1.inFun();}
}
class Outer{private static String a 外部类的静态a;private static String b 外部类的静态b;private String c 外部类对象的非静态c;private String d 外部类对象的非静态d;static class StaticInner{private static String a 静态内部类的静态a;private String c 静态内部类对象的非静态c;public static void inMethod(){System.out.println(Inner.a a);System.out.println(Outer.a Outer.a);System.out.println(b b);}public void inFun(){System.out.println(Inner.inFun);System.out.println(Outer.a Outer.a);System.out.println(Inner.a a);System.out.println(b b);System.out.println(c c);
// System.out.println(d d);//不能访问外部类的非静态成员}}class NoStaticInner{private String a 非静态内部类对象的非静态a;private String c 非静态内部类对象的非静态c;public void inFun(){System.out.println(NoStaticInner.inFun);System.out.println(Outer.a Outer.a);System.out.println(a a);System.out.println(b b);System.out.println(Outer.c Outer.this.c);System.out.println(c c);System.out.println(d d);}}public NoStaticInner getNoStaticInner(){return new NoStaticInner();}
}8.3 局部内部类
8.3.1 非匿名局部内部类
语法格式
[修饰符] class 外部类{[修饰符] 返回值类型 方法名(形参列表){[final/abstract] class 内部类{}}
}编译后有自己的独立的字节码文件只不过在内部类名前面冠以外部类名、$符号、编号。 这里有编号是因为同一个外部类中不同的方法中存在相同名称的局部内部类
和成员内部类不同的是它前面不能有权限修饰符等局部内部类如同局部变量一样有作用域局部内部类中是否能访问外部类的非静态的成员取决于所在的方法
举例
public class TestLocalInner {public static void main(String[] args) {Outer.outMethod();System.out.println(-------------------);Outer out new Outer();out.outTest();System.out.println(-------------------);Runner runner Outer.getRunner();runner.run();}
}
class Outer{public static void outMethod(){System.out.println(Outer.outMethod);final String c 局部变量c;class Inner{public void inMethod(){System.out.println(Inner.inMethod);System.out.println(c);}}Inner in new Inner();in.inMethod();}public void outTest(){class Inner{public void inMethod1(){System.out.println(Inner.inMethod1);}}Inner in new Inner();in.inMethod1();}public static Runner getRunner(){class LocalRunner implements Runner{Overridepublic void run() {System.out.println(LocalRunner.run);}}return new LocalRunner();}}
interface Runner{void run();
}8.3.2 匿名内部类
因为考虑到这个子类或实现类是一次性的那么我们“费尽心机”的给它取名字就显得多余。那么我们完全可以使用匿名内部类的方式来实现避免给类命名的问题。
new 父类([实参列表]){重写方法...
}new 父接口(){重写方法...
}举例1使用匿名内部类的对象直接调用方法
interface A{void a();
}
public class Test{public static void main(String[] args){new A(){Overridepublic void a() {System.out.println(aaaa);}}.a();}
}举例2通过父类或父接口的变量多态引用匿名内部类的对象
interface A{void a();
}
public class Test{public static void main(String[] args){A obj new A(){Overridepublic void a() {System.out.println(aaaa);}};obj.a();}
}举例3匿名内部类的对象作为实参
interface A{void method();
}
public class Test{public static void test(A a){a.method();}public static void main(String[] args){test(new A(){Overridepublic void method() {System.out.println(aaaa);}});}
}8.4 练习
练习判断输出结果为何
public class Test {public Test() {Inner s1 new Inner();s1.a 10;Inner s2 new Inner();s2.a 20;Test.Inner s3 new Test.Inner();System.out.println(s3.a);}class Inner {public int a 5;}public static void main(String[] args) {Test t new Test();Inner r t.new Inner();System.out.println(r.a);}
}
练习2
编写一个匿名内部类它继承Object并在匿名内部类中声明一个方法public void test()打印尚硅谷。
请编写代码调用这个方法。
package com.atguigu.test01;public class Test01 {public static void main(String[] args) {new Object(){public void test(){System.out.println(尚硅谷);}}.test();}
}
9. 枚举类
9.1 概述 枚举类型本质上也是一种类只不过是这个类的对象是有限的、固定的几个不能让用户随意创建。 枚举类的例子举不胜举 星期Monday(星期一)…Sunday(星期天)性别Man(男)、Woman(女)月份January(1月)…December(12月)季节Spring(春节)…Winter(冬天)三原色red(红色)、green(绿色)、blue(蓝色)支付方式Cash现金、WeChatPay微信、Alipay(支付宝)、BankCard(银行卡)、CreditCard(信用卡)就职状态Busy(忙碌)、Free(空闲)、Vocation(休假)、Dimission(离职)订单状态Nonpayment未付款、Paid已付款、Fulfilled已配货、Delivered已发货、Checked已确认收货、Return退货、Exchange换货、Cancel取消线程状态创建、就绪、运行、阻塞、死亡 若枚举只有一个对象, 则可以作为一种单例模式的实现方式。 枚举类的实现 在JDK5.0 之前需要程序员自定义枚举类型。在JDK5.0 之后Java支持enum关键字来快速定义枚举类型。
9.2 定义枚举类JDK5.0 之前
在JDK5.0 之前如何声明枚举类呢
私有化类的构造器保证不能在类的外部创建其对象在类的内部创建枚举类的实例。声明为public static final 对外暴露这些常量对象对象如果有实例变量应该声明为private final建议不是必须并在构造器中初始化
示例代码
class Season{private final String SEASONNAME;//季节的名称private final String SEASONDESC;//季节的描述private Season(String seasonName,String seasonDesc){this.SEASONNAME seasonName;this.SEASONDESC seasonDesc;}public static final Season SPRING new Season(春天, 春暖花开);public static final Season SUMMER new Season(夏天, 夏日炎炎);public static final Season AUTUMN new Season(秋天, 秋高气爽);public static final Season WINTER new Season(冬天, 白雪皑皑);Overridepublic String toString() {return Season{ SEASONNAME SEASONNAME \ , SEASONDESC SEASONDESC \ };}
}
class SeasonTest{public static void main(String[] args) {System.out.println(Season.AUTUMN);}
}9.3 定义枚举类JDK5.0 之后
9.3.1 enum关键字声明枚举
【修饰符】 enum 枚举类名{常量对象列表
}【修饰符】 enum 枚举类名{常量对象列表;对象的实例变量列表;
}举例1
package com.atguigu.enumeration;public enum Week {MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY;
}public class TestEnum {public static void main(String[] args) {Season spring Season.SPRING;System.out.println(spring);}
}9.3.2 enum方式定义的要求和特点
枚举类的常量对象列表必须在枚举类的首行因为是常量所以建议大写。列出的实例系统会自动添加 public static final 修饰。如果常量对象列表后面没有其他代码那么“”可以省略否则不可以省略“”。编译器给枚举类默认提供的是private的无参构造如果枚举类需要的是无参构造就不需要声明写常量对象列表时也不用加参数如果枚举类需要的是有参构造需要手动定义有参构造的private可以省略调用有参构造的方法就是在常量对象名后面加(实参列表)就可以。枚举类默认继承的是java.lang.Enum类因此不能再继承其他的类型。JDK5.0 之后switch提供支持枚举类型case后面可以写枚举常量名无需添加枚举类作为限定。
举例2
public enum SeasonEnum {SPRING(春天,春风又绿江南岸),SUMMER(夏天,映日荷花别样红),AUTUMN(秋天,秋水共长天一色),WINTER(冬天,窗含西岭千秋雪);private final String seasonName;private final String seasonDesc;private SeasonEnum(String seasonName, String seasonDesc) {this.seasonName seasonName;this.seasonDesc seasonDesc;}public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}
}
举例3
package com.atguigu.enumeration;public enum Week {MONDAY(星期一),TUESDAY(星期二),WEDNESDAY(星期三),THURSDAY(星期四),FRIDAY(星期五),SATURDAY(星期六),SUNDAY(星期日);private final String description;private Week(String description){this.description description;}Overridepublic String toString() {return super.toString() : description;}
}package com.atguigu.enumeration;public class TestWeek {public static void main(String[] args) {Week week Week.MONDAY;System.out.println(week);switch (week){case MONDAY:System.out.println(怀念周末困意很浓);break;case TUESDAY:System.out.println(进入学习状态);break;case WEDNESDAY:System.out.println(死撑);break;case THURSDAY:System.out.println(小放松);break;case FRIDAY:System.out.println(又信心满满);break;case SATURDAY:System.out.println(开始盼周末无心学习);break;case SUNDAY:System.out.println(一觉到下午);break;}}
}经验之谈 开发中当需要定义一组常量时强烈建议使用枚举类。 9.4 enum中常用方法
String toString(): 默认返回的是常量名对象名可以继续手动重写该方法static 枚举类型[] values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值是一个静态方法static 枚举类型 valueOf(String name)可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是会有运行时异常IllegalArgumentException。String name():得到当前枚举常量的名称。建议优先使用toString()。int ordinal():返回当前枚举常量的次序号默认从0开始
举例
package com.atguigu.enumeration;import java.util.Scanner;public class TestEnumMethod {public static void main(String[] args) {//values()Week[] values Week.values();for (int i 0; i values.length; i) {//ordinal()、name()System.out.println((values[i].ordinal()1) - values[i].name());}System.out.println(------------------------);Scanner input new Scanner(System.in);System.out.print(请输入星期值);int weekValue input.nextInt();Week week values[weekValue-1];//toString()System.out.println(week);System.out.print(请输入星期名);String weekName input.next();//valueOf()week Week.valueOf(weekName);System.out.println(week);input.close();}
}9.5 实现接口的枚举类
和普通 Java 类一样枚举类可以实现一个或多个接口若每个枚举值在调用实现的接口方法呈现相同的行为方式则只要统一实现该方法即可。若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式则可以让每个枚举值分别来实现该方法
语法
//1、枚举类可以像普通的类一样实现接口并且可以多个但要求必须实现里面所有的抽象方法
enum A implements 接口1接口2{//抽象方法的实现
}//2、如果枚举类的常量可以继续重写抽象方法!
enum A implements 接口1接口2{常量名1(参数){//抽象方法的实现或重写},常量名2(参数){//抽象方法的实现或重写},//...
}举例
interface Info{void show();
}//使用enum关键字定义枚举类
enum Season1 implements Info{//1. 创建枚举类中的对象,声明在enum枚举类的首位SPRING(春天,春暖花开){public void show(){System.out.println(春天在哪里);}},SUMMER(夏天,夏日炎炎){public void show(){System.out.println(宁静的夏天);}},AUTUMN(秋天,秋高气爽){public void show(){System.out.println(秋天是用来分手的季节);}},WINTER(冬天,白雪皑皑){public void show(){System.out.println(2002年的第一场雪);}};//2. 声明每个对象拥有的属性:private final修饰private final String SEASON_NAME;private final String SEASON_DESC;//3. 私有化类的构造器private Season1(String seasonName,String seasonDesc){this.SEASON_NAME seasonName;this.SEASON_DESC seasonDesc;}public String getSEASON_NAME() {return SEASON_NAME;}public String getSEASON_DESC() {return SEASON_DESC;}
}10. 注解(Annotation)
10.1 注解概述
10.1.1 什么是注解
注解Annotation是从JDK5.0开始引入以“注解名”在代码中存在。例如
OverrideDeprecatedSuppressWarnings(value”unchecked”)Annotation 可以像修饰符一样被使用可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。还可以添加一些参数值这些信息被保存在 Annotation 的 “namevalue” 对中。
注解可以在类编译、运行时进行加载体现不同的功能。
10.1.2 注解与注释
注解也可以看做是一种注释通过使用 Annotation程序员可以在不改变原有逻辑的情况下在源文件中嵌入一些补充信息。但是注解不同于单行注释和多行注释。
对于单行注释和多行注释是给程序员看的。而注解是可以被编译器或其他程序读取的。程序还可以根据注解的不同做出相应的处理。
10.1.3 注解的重要性
在JavaSE中注解的使用目的比较简单例如标记过时的功能忽略警告等。在JavaEE/Android中注解占据了更重要的角色例如用来配置应用程序的任何切面代替JavaEE旧版中所遗留的繁冗代码和XML配置等。
未来的开发模式都是基于注解的JPA是基于注解的Spring2.5以上都是基于注解的Hibernate3.x以后也是基于注解的Struts2有一部分也是基于注解的了。注解是一种趋势一定程度上可以说框架 注解 反射 设计模式。
10.2 常见的Annotation作用
示例1生成文档相关的注解
author 标明开发该类模块的作者多个作者之间使用,分割
version 标明该类模块的版本
see 参考转向也就是相关主题
since 从哪个版本开始增加的
param 对方法中某参数的说明如果没有参数就不能写
return 对方法返回值的说明如果方法的返回值类型是void就不能写
exception 对方法可能抛出的异常进行说明 如果方法没有用throws显式抛出的异常就不能写package com.annotation.javadoc;
public class JavadocTest {/*** 程序的主方法程序的入口* param args String[] 命令行参数*/public static void main(String[] args) {}/*** 求圆面积的方法* param radius double 半径值* return double 圆的面积*/public static double getArea(double radius){return Math.PI * radius * radius;}
}
示例2在编译时进行格式检查(JDK内置的三个基本注解)
Override: 限定重写父类方法该注解只能用于方法
Deprecated: 用于表示所修饰的元素(类方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
SuppressWarnings: 抑制编译器警告
package com.annotation.javadoc;public class AnnotationTest{public static void main(String[] args) {SuppressWarnings(unused)int a 10;}Deprecatedpublic void print(){System.out.println(过时的方法);}Overridepublic String toString() {return 重写的toString方法();}
}
示例3跟踪代码依赖性实现替代配置文件功能
Servlet3.0提供了注解(annotation)使得不再需要在web.xml文件中进行Servlet的部署。
WebServlet(/login)
public class LoginServlet extends HttpServlet {private static final long serialVersionUID 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) { }protected void doPost(HttpServletRequest request, HttpServletResponse response) {doGet(request, response);}
} servletservlet-nameLoginServlet/servlet-nameservlet-classcom.servlet.LoginServlet/servlet-class/servletservlet-mappingservlet-nameLoginServlet/servlet-nameurl-pattern/login/url-pattern/servlet-mapping
Spring框架中关于“事务”的管理
Transactional(propagationPropagation.REQUIRES_NEW,isolationIsolation.READ_COMMITTED,readOnlyfalse,timeout3)
public void buyBook(String username, String isbn) {//1.查询书的单价int price bookShopDao.findBookPriceByIsbn(isbn);//2. 更新库存bookShopDao.updateBookStock(isbn); //3. 更新用户的余额bookShopDao.updateUserAccount(username, price);
}
!-- 配置事务属性 --
tx:advice transaction-managerdataSourceTransactionManager idtxAdvicetx:attributes!-- 配置每个方法使用的事务属性 --tx:method namebuyBook propagationREQUIRES_NEW isolationREAD_COMMITTED read-onlyfalse timeout3 //tx:attributes
/tx:advice
10.3 三个最基本的注解
10.3.1 Override 用于检测被标记的方法为有效的重写方法如果不是则报编译错误 只能标记在方法上。 它会被编译器程序读取。
10.3.2 Deprecated 用于表示被标记的数据已经过时不推荐使用。 可以用于修饰 属性、方法、构造、类、包、局部变量、参数。 它会被编译器程序读取。
10.3.3 SuppressWarnings 抑制编译警告。当我们不希望看到警告信息的时候可以使用 SuppressWarnings 注解来抑制警告信息 可以用于修饰类、属性、方法、构造、局部变量、参数 它会被编译器程序读取。 可以指定的警告类型有了解 all抑制所有警告unchecked抑制与未检查的作业相关的警告unused抑制与未用的程式码及停用的程式码相关的警告deprecation抑制与淘汰的相关警告nls抑制与非 nls 字串文字相关的警告null抑制与空值分析相关的警告rawtypes抑制与使用 raw 类型相关的警告static-access抑制与静态存取不正确相关的警告static-method抑制与可能宣告为 static 的方法相关的警告super抑制与置换方法相关但不含 super 呼叫的警告…
示例代码
package com.atguigu.annotation;import java.util.ArrayList;public class TestAnnotation {SuppressWarnings(all)public static void main(String[] args) {int i;ArrayList list new ArrayList();list.add(hello);list.add(123);list.add(world);Father f new Son();f.show();f.methodOl();}
}class Father{Deprecatedvoid show() {System.out.println(Father.show);}void methodOl() {System.out.println(Father Method);}
}class Son extends Father{
/* Overridevoid method01() {System.out.println(Son Method);}*/
}10.4 元注解
JDK1.5在java.lang.annotation包定义了4个标准的meta-annotation类型它们被用来提供对其它 annotation类型作说明。
1**Target**用于描述注解的使用范围
可以通过枚举类型ElementType的10个常量对象来指定TYPEMETHODCONSTRUCTORPACKAGE…
2**Retention**用于描述注解的生命周期
可以通过枚举类型RetentionPolicy的3个常量对象来指定SOURCE源代码、CLASS字节码、RUNTIME运行时唯有RUNTIME阶段才能被反射读取到。
3Documented表明这个注解应该被 javadoc工具记录。
4**Inherited**允许子类继承父类中的注解
示例代码
package java.lang;import java.lang.annotation.*;Target(ElementType.METHOD)
Retention(RetentionPolicy.SOURCE)
public interface Override {
}package java.lang;import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
Retention(RetentionPolicy.SOURCE)
public interface SuppressWarnings {String[] value();
}package java.lang;import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;Documented
Retention(RetentionPolicy.RUNTIME)
Target(value{CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public interface Deprecated {
}拓展元数据 String name “Tom”; 10.5 自定义注解的使用
一个完整的注解应该包含三个部分 1声明 2使用 3读取
10.5.1 声明自定义注解
【元注解】
【修饰符】 interface 注解名{【成员列表】
}自定义注解可以通过四个元注解Retention,TargetInherited,Documented分别说明它的声明周期使用位置是否被继承是否被生成到API文档中。Annotation 的成员在 Annotation 定义中以无参数有返回值的抽象方法的形式来声明我们又称为配置参数。返回值类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组可以使用 default 关键字为抽象方法指定默认返回值如果定义的注解含有抽象方法那么使用时必须指定返回值除非它有默认值。格式是“方法名 返回值”如果只有一个抽象方法需要赋值且方法名为value可以省略“value”所以如果注解只有一个抽象方法成员建议使用方法名value。
package com.atguigu.annotation;import java.lang.annotation.*;Inherited
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
public interface Table {String value();
}package com.atguigu.annotation;import java.lang.annotation.*;Inherited
Target(ElementType.FIELD)
Retention(RetentionPolicy.RUNTIME)
public interface Column {String columnName();String columnType();
}10.5.2 使用自定义注解
package com.atguigu.annotation;Table(t_stu)
public class Student {Column(columnName sid,columnType int)private int id;Column(columnName sname,columnType varchar(20))private String name;public int getId() {return id;}public void setId(int id) {this.id id;}public String getName() {return name;}public void setName(String name) {this.name name;}Overridepublic String toString() {return Student{ id id , name name \ };}
}
10.5.3 读取和处理自定义注解
自定义注解必须配上注解的信息处理流程才有意义。
我们自己定义的注解只能使用反射的代码读取。所以自定义注解的声明周期必须是RetentionPolicy.RUNTIME。
具体的使用见《尚硅谷_宋红康_第17章_反射机制.md》。
10.6 JUnit单元测试
10.6.1 测试分类
**黑盒测试**不需要写代码给输入值看程序是否能够输出期望的值。
**白盒测试**需要写代码的。关注程序具体的执行流程。 10.6.2 JUnit单元测试介绍
JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个测试框架regression testing framework供Java开发人员编写单元测试之用。
JUnit测试是程序员测试即所谓白盒测试因为程序员知道被测试的软件如何How完成功能和完成什么样What的功能。
要使用JUnit必须在项目的编译路径中引入JUnit的库即相关的.class文件组成的jar包。jar就是一个压缩包压缩包都是开发好的第三方Oracle公司第一方我们自己第二方其他都是第三方工具类都是以class文件形式存在的。
10.6.3 引入本地JUnit.jar
第1步在项目中File-Project Structure中操作添加Libraries库 其中junit-libs包内容如下 第2步选择要在哪些module中应用JUnit库 第3步检查是否应用成功 注意Scope选择Compile否则编译时无法使用JUnit。
第4步下次如果有新的模块要使用该libs库这样操作即可 10.6.4 编写和运行Test单元测试方法
JUnit4版本要求Test标记的方法必须满足如下要求
所在的类必须是public的非抽象的包含唯一的无参构造器。Test标记的方法本身必须是public非抽象的非静态的void无返回值()无参数的。
package com.atguigu.junit;import org.junit.Test;public class TestJUnit {Testpublic void test01(){System.out.println(TestJUnit.test01);}Testpublic void test02(){System.out.println(TestJUnit.test02);}Testpublic void test03(){System.out.println(TestJUnit.test03);}
}10.6.5 设置执行JUnit用例时支持控制台输入
1. 设置数据
默认情况下在单元测试方法中使用Scanner时并不能实现控制台数据的输入。需要做如下设置
在idea64.exe.vmoptions配置文件中加入下面一行设置重启idea后生效。
-Deditable.java.test.consoletrue2. 配置文件位置 添加完成之后重启IDEA即可。
3. 如果上述位置设置不成功需要继续修改如下位置
修改位置1IDEA安装目录的bin目录例如D:\develop_tools\IDEA\IntelliJ IDEA 2022.1.2\bin下的idea64.exe.vmoptions文件。
修改位置2C盘的用户目录C:\Users\用户名\AppData\Roaming\JetBrains\IntelliJIdea2022.1 下的idea64.exe.vmoptions件。
10.6.6 定义test测试方法模板
选中自定义的模板组点击””1.Live Template来定义模板。 11. 包装类
11.1 为什么需要包装类
Java提供了两个类型系统基本数据类型与引用数据类型。使用基本数据类型在于效率然而当要使用只针对对象设计的API或新特性例如泛型怎么办呢例如
//情况1方法形参
Object类的equals(Object obj)//情况2方法形参
ArrayList类的add(Object obj)
//没有如下的方法
add(int number)
add(double d)
add(boolean b)//情况3泛型
SetT
ListT
CllectionT
MapK,V11.2 有哪些包装类
Java针对八种基本数据类型定义了相应的引用类型包装类封装类。有了类的特点就可以调用类中的方法Java才是真正的面向对象。 封装以后的内存结构对比
public static void main(String[] args){int num 520;Integer obj new Integer(520);
}11.3 自定义包装类
public class MyInteger {int value;public MyInteger() {}public MyInteger(int value) {this.value value;}Overridepublic String toString() {return String.valueOf(value);}
}
11.4 包装类与基本数据类型间的转换
11.4.1 装箱
装箱把基本数据类型转为包装类对象 转为包装类的对象是为了使用专门为对象设计的API和特性 基本数值----包装对象
Integer obj1 new Integer(4);//使用构造函数函数
Float f new Float(“4.56”);
Long l new Long(“asdf”); //NumberFormatExceptionInteger obj2 Integer.valueOf(4);//使用包装类中的valueOf方法11.4.2 拆箱
拆箱把包装类对象拆为基本数据类型 转为基本数据类型一般是因为需要运算Java中的大多数运算符是为基本数据类型设计的。比较、算术等 包装对象----基本数值
Integer obj new Integer(4);
int num1 obj.intValue();自动装箱与拆箱
由于我们经常要做基本类型与包装类之间的转换从JDK5.0 开始基本类型与包装类的装箱、拆箱动作可以自动完成。例如
Integer i 4;//自动装箱。相当于Integer i Integer.valueOf(4);
i i 5;//等号右边将i对象转成基本数值(自动拆箱) i.intValue() 5;
//加法运算完成后再次装箱把基本数值转成对象。注意只能与自己对应的类型之间才能实现自动装箱与拆箱。 Integer i 1;
Double d 1;//错误的1是int类型11.5 基本数据类型、包装类与字符串间的转换
1基本数据类型转为字符串
**方式1**调用字符串重载的valueOf()方法
int a 10;
//String str a;//错误的String str String.valueOf(a);**方式2**更直接的方式
int a 10;String str a ;2字符串转为基本数据类型
**方式1**除了Character类之外其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型例如
public static int parseInt(String s)将字符串参数转换为对应的int基本类型。public static long parseLong(String s)将字符串参数转换为对应的long基本类型。public static double parseDouble(String s)将字符串参数转换为对应的double基本类型。
**方式2**字符串转为包装类然后可以自动拆箱为基本数据类型
public static Integer valueOf(String s)将字符串参数转换为对应的Integer包装类然后可以自动拆箱为int基本类型public static Long valueOf(String s)将字符串参数转换为对应的Long包装类然后可以自动拆箱为long基本类型public static Double valueOf(String s)将字符串参数转换为对应的Double包装类然后可以自动拆箱为double基本类型
注意:如果字符串参数的内容无法正确转换为对应的基本类型则会抛出java.lang.NumberFormatException异常。
**方式3**通过包装类的构造器实现
int a Integer.parseInt(整数的字符串);
double d Double.parseDouble(小数的字符串);
boolean b Boolean.parseBoolean(true或false);int a Integer.valueOf(整数的字符串);
double d Double.valueOf(小数的字符串);
boolean b Boolean.valueOf(true或false);int i new Integer(“12”);
其他方式小结 11.6 包装类的其它API
11.6.1 数据类型的最大最小值
Integer.MAX_VALUE和Integer.MIN_VALUELong.MAX_VALUE和Long.MIN_VALUEDouble.MAX_VALUE和Double.MIN_VALUE11.6.2 字符转大小写
Character.toUpperCase(x);Character.toLowerCase(X);11.6.3 整数转进制
Integer.toBinaryString(int i) Integer.toHexString(int i)Integer.toOctalString(int i)11.6.4 比较的方法
Double.compare(double d1, double d2)Integer.compare(int x, int y) 11.7 包装类对象的特点
11.7.1 包装类缓存对象
包装类缓存对象Byte-128~127Short-128~127Integer-128~127Long-128~127Float没有Double没有Character0~127Booleantrue和false
Integer a 1;
Integer b 1;
System.out.println(a b);//trueInteger i 128;
Integer j 128;
System.out.println(i j);//falseInteger m new Integer(1);//新new的在堆中
Integer n 1;//这个用的是缓冲的常量对象在方法区
System.out.println(m n);//falseInteger x new Integer(1);//新new的在堆中
Integer y new Integer(1);//另一个新new的在堆中
System.out.println(x y);//falseDouble d1 1.0;
Double d2 1.0;
System.out.println(d1d2);//false 比较地址没有缓存对象每一个都是新new的11.7.2 类型转换问题
Integer i 1000;
double j 1000;
System.out.println(ij);//true 会先将i自动拆箱为int然后根据基本数据类型“自动类型转换”规则转为double比较Integer i 1000;
int j 1000;
System.out.println(ij);//true 会自动拆箱按照基本数据类型进行比较Integer i 1;
Double d 1.0
System.out.println(id);//编译报错11.7.3 包装类对象不可变
public class TestExam {public static void main(String[] args) {int i 1;Integer j new Integer(2);Circle c new Circle();change(i,j,c);System.out.println(i i);//1System.out.println(j j);//2System.out.println(c.radius c.radius);//10.0}/** 方法的参数传递机制* 1基本数据类型形参的修改完全不影响实参* 2引用数据类型通过形参修改对象的属性值会影响实参的属性值* 这类Integer等包装类对象是“不可变”对象即一旦修改就是新对象和实参就无关了*/public static void change(int a ,Integer b,Circle c ){a 10;
// b 10;//等价于 b new Integer(b10);c.radius 10;/*c new Circle();c.radius10;*/}
}
class Circle{double radius;
}11.8 练习
笔试题如下两个题目输出结果相同吗各是什么。
Object o1 true ? new Integer(1) : new Double(2.0);
System.out.println(o1);//1.0
Object o2;
if (true)o2 new Integer(1);
elseo2 new Double(2.0);
System.out.println(o2);//1
面试题
public void method1() {Integer i new Integer(1);Integer j new Integer(1);System.out.println(i j);Integer m 1;Integer n 1;System.out.println(m n);//Integer x 128;Integer y 128;System.out.println(x y);//
}
练习
利用Vector代替数组处理从键盘读入学生成绩以负数代表输入结束找出最高分并输出学生成绩等级。 提示数组一旦创建长度就固定不变所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动态伸缩。 创建Vector对象Vector vnew Vector(); 给向量添加元素v.addElement(Object obj); //obj必须是对象 取出向量中的元素Object objv.elementAt(0); 注意第一个元素的下标是0返回值是Object类型的。 计算向量的长度v.size(); 若与最高分相差10分内A等20分内B等30分内C等其它D等 过包装类的构造器实现