关键字:static
类变量
静态变量的内存解析:
相关代码:
public class ChineseTest {
public static void main(String[] args) {
System.out.println(Chinese.nation); //null 没赋值前
System.out.println(Chinese.nation); //中国 静态变量赋值之后
Chinese c1 = new Chinese();
c1.name = "姚明";
c1.age = 40;
c1.nation = "China";
Chinese c2 = new Chinese();
c2.name = "刘翔";
c2.age = 39;
System.out.println(c1);
System.out.println(c2);
System.out.println(c1.nation); // China
System.out.println(c2.nation); // China
c2.nation = "CHN";
System.out.println(c1.nation); // CHN
System.out.println(c2.nation); // CHN
}
}
class Chinese { //中国人类
//非静态变量、实例变量
String name;
int age;
//静态变量、类变量
/**
* China
* China
* CHN
* CHN
*/
static String nation = "中国";
/**
* China
* null
* China
* CHN
*/
// String nation;
@Override
public String toString() {
return "Chinese{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
类方法(class method)
案例1:
编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额"、“利率"、“最小余额", 定义封装这些属性的方法,账号要自动生成。 编写主类,使用银行账户类,输入、输出3个储户的上述信息。 考虑:哪些属性可以设计成static属性x
相关代码:
AccountTest
public class AccountTest {
public static void main(String[] args) {
Account acct1 = new Account();
System.out.println(acct1);
Account acct2 = new Account("123456", 2000);
System.out.println(acct2);
Account.setInterestRate(0.0123);
Account.setMinBalance(10);
System.out.println("银行存款的利率为:" + Account.getInterestRate());
System.out.println("银行存款的额度为:" + Account.getMinBalance());
}
}
Account
public class Account {
private int id; //账号
private String password; //密码
private double balance; //余额
private static double interestRate; //利率
private static double minBalance = 1.0; //最小余额
private static int init = 1001; //用于自动生成id的基数
public Account(){
this.id = init;
init++;
password = "000000";
}
public Account(String password, double balance) {
this.password = password;
this.balance = balance;
this.id = init;
init++;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public static double getInterestRate() {
return interestRate;
}
public static void setInterestRate(double interestRate) {
Account.interestRate = interestRate;
}
public static double getMinBalance() {
return minBalance;
}
public static void setMinBalance(double minBalance) {
Account.minBalance = minBalance;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", password='" + password + '\'' +
", balance=" + balance +
'}';
}
}
面试题:
public class StaticTest {
public static void main(String[] args) {
Order order = null;
//可以打印出来,静态并不依赖于对象,直接通过类调用即可
order.hello(); //hello!
System.out.println(order.count); //1
}
}
class Order{
public static int count = 1;
public static void hello() {
System.out.println("hello!");
}
}
本章总结:
static关键字的使用 1. static:静态的 2.static 用来修饰的结构:属性、方法;代码块、内部类 3.static修饰属性 3.1 复习:变量的分类 方式1:按照数据类型:基本数据类型、引用数据类型 方式2:按照类中声明的位置 成员变量:按照是否使用static修饰进行分类: 使用static修饰的成员变量:静态变量、类变量 不使用static修饰的成员变量:非静态变量、实例变量 局部变量:方法内、方法形参、构造器内、构造器形参、代码块内等。 3.2 静态变量:类中的属性使用static进行修饰。 对比静态变量与实例变量: ① 个数 > 静态变量: 内存空间中只有一份,被类的多个对象所共享。 > 实例变量: 类的每一个实例(或对象)都保存着一份实例变量。 ② 内存位置 > 静态变量: jdk6及之前:存放在方法区。jdk7之后:存放在堆空间 > 实例变量: 存放在堆空间的对象实体中 ③ 加载时机 > 静态变量: 随着类的加载而加载,由于类只会加载一次,所以静态变量也只有一份。 > 实例变量: 随着对象的创建而加载。每个对象拥有一份实例变量。 ④ 调用者 > 静态变量: 可以被类直接调用,也可以使用对象调用。 > 实例变量: 只能使用对象进行调用。 ⑤ 判断是否可以调用 ---> 从生命周期的角度解释 类变量 实例变量 类 yes no 对象 yes yes ⑥ 消亡时机 > 静态变量: 随着类的卸载而消亡 > 实例变量: 随着对象的消亡而消亡 4.static修饰方法:(类方法、静态方法) > 随着类的加载而加载 > 可以通过"类.静态方法"的方式,直接调用静态方法 > 静态方法内可以调用静态的属性或静态的方法(属性和方法的前缀使用的是当前类,可以省略) 不可以调用非静态的结构。(比如:属性、方法) 类变量 实例变量 类 yes no 对象 yes yes > static修饰的方法内,不能使用this和super > 补充:在类的非静态方法中,可以调用当前类中的静态结构(属性、方法)或非静态结构(属性、方法) 5.开发中,什么时候需要将属性声明为静态的? > 判断当前类的多个实例是否能共享此成员变量,且此成员变量的值是相同的。 > 开发中,常将一些常量声明是静态的。比如:Math类中的PI 什么时候需要将方法声明为静态的? > 方法内操作的变量如果都是静态变量(而非实例变量)的话,则此方法建议声明为静态方法 > 开发中,常常将工具类中的方法,声明为静态方法。比如:Arrays类、Math类
单例 (Singleton)设计模式
单例模式和实现思路(如果我们要让类在一个虚拟机...):
单例(Singleton)设计模式-饿汉式
单例(Singleton)设计模式-懒汉式
单例模式的优点
总结:
1.设计模式概述: 设计模式是在大量的`实践中总结`和`理论化`之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索. 就像是经典的棋谱,不同的棋局,我们用不同的棋谱。"套路" 经典的设计模式一共有23种。 2.何为单例模式(Singleton): 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。 3.如何实现单例模式(掌握): > 饿汉式 > 懒汉式 4.对比两种模式(特点、优缺点) 特点: > 饿汉式:“立即加载”,随着类的加载,当前的唯一实例就创建了 > 懒汉式:"延迟加载",在需要使用的时候,进行创建。 优缺点: > 饿汉式:(优点)写法简单,由于内存中较早加载,使用更方便、更快。是线程安全的。(缺点)内存中占用时间较长。 > 懒汉式: (缺点)线程不安全 (放到多线程章节时解决)(优点)在需要的时候进行创建,节省内存空间。
实例代码:
//饿汉式
public class BankTest {
public static void main(String[] args) {
// Bank bank1 = new Bank();
// Bank bank2 = new Bank();
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1 == bank2); //true
}
}
//饿汉式
class Bank {
//1.类的构造器石私有化
private Bank(){
}
//2.在类的内部创建当前类的实例
//4.此属性也必须声明为static的
private static Bank instance = new Bank();
//3.使用getXXX()方法获取当前类的实例,必须声明为static的
public static Bank getInstance(){
return instance;
}
}
//懒汉式
public class GirlFriendTest {
public static void main(String[] args) {
GirlFriend test1 = GirlFriend.getInstance();
GirlFriend test2 = GirlFriend.getInstance();
System.out.println(test1 == test2); //true
}
}
class GirlFriend {
//1.类的构造器私有化
private GirlFriend() {
}
//2.声明当前类的实例化
//4.此属性也必须声明为static的
private static GirlFriend instance = null;
//3.通过getXXX()获取当前类的实例,如果未创建对象,则在方法内部进行创建
public static GirlFriend getInstance(){
if(instance == null){
instance = new GirlFriend();
}
return instance;
}
}
理解main方法的语法
方式2:使用main()的形参进行传值。 代码:
public class MainDemo {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("hello:" + args[i]);
}
}
}
命令行的方式传值:在执行命令后空格 写上实参![]()
idea的传值方式
点击编辑配置:
执行结果:
总结:
main()方法的剖析 public static void main(string args[]){} 1.理解1: 看做是一个普通的静态方法 理解2: 看做是程序的入口,格式是固定的。 2.与控制台交互 如何从键盘获取数据? > 方式1:使用Scanner > 方式2:使用main()的形参进行传值。 命令行的方式传值 idea的传值方式
相关代码:
MainTest
public class MainTest {
public static void main(String[] args) { //程序的入口
String[] arr = new String[]{"AA", "BB", "CC"};
Main.main(arr);
}
}
class Main{
public static void main(String[] args) { //看做是普通的静态方法
System.out.println("Main的main()的调用");
for(int i = 0; i < args.length; i++){
System.out.println(args[i]);
}
/**
* Main的main()的调用
* AA
* BB
* CC
*/
}
}
类的成员之四:代码块
总结:
类的成员之四:代码块 回顾:类中可以声明的结构:属性、方法、构造器;代码块(或初始化块)、内部类 1.代码块(或初始化块)的作用: (即初始化类或对象的成员变量)用来初始化类或对象的信息 2.代码块的修饰: 只能使用static进行修饰。 3.代码块的分类: 静态代码块:使用static修饰 非静态代码块:没有使用static修饰 4.具体使用: 4.1 静态代码块: > 随着类的加载而执行 > 由于类的加载只会执行一次,进而静态代码块的执行,也只会执行一次 > 作用:用来初始化类的信息 > 内部可以声明变量、调用属性或方法、编写输出语句等操作。 > 静态代码块的执行要先于非静态代码块的执行 > 如果声明有多个静态代码块,则按照声明的先后顺序执行 > 静态代码块内部只能调用静态的结构(即静态的属性、方法),不能调用非静态的结构(即非静态的属性、方法) 4.2 非静态代码块: > 随着对象的创建而执行 > 每创建当前类的一个实例,就会执行一次非静态代码块 > 作用:用来初始化对象的信息 > 内部可以声明变量、调用属性或方法、编写输出语句等操作。 > 如果声明有多个非静态代码块,则按照声明的先后顺序执行 >非静态代码块内部可以调用静态的结构(即静态的属性、方法),也可以调用非静态的结构(即非静态的属性、方法)
代码:
(1)声明User类 - 包含属性:userName(String类型),password(String类型),registrationTime(long类型),私有化 - 包含get/set方法,其中registrationTime没有set方法 - 包含无参构造, - 输出“新用户注册”, - registrationTime赋值为当前系统时间 - userName就默认为当前系统时间值, - password默认为"123456" - 包含有参构造(String userName,String password) - 输出“新用户注册”, - registrationTime赋值为当前系统时间, - username和password由参数赋值 - 包含public String getInfo()方法,返回:"用户名:xx,密码:xx,注册时间:xx" (2)编写测试类,测试类main方法的代码
UserTest
public class UserTest {
public static void main(String[] args) {
User u1 = new User();
System.out.println(u1.getInfo());
User u2 = new User("Tom","654321");
System.out.println(u2.getInfo());
System.out.println();
User1 u3 = new User1();
System.out.println(u3.getInfo());
}
}
User1
public class User1 {
private String userName;
private String password;
private long registrationTime; //注册时间
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;
}
{
System.out.println("新用户注册");
registrationTime = System.currentTimeMillis(); //获取系统当前时间(距离1970-1-1 00:00:00的毫秒数)
}
//代码块的使用
public User1(){
// System.out.println("新用户注册");
// registrationTime = System.currentTimeMillis(); //获取系统当前时间(距离1970-1-1 00:00:00的毫秒数)
userName = System.currentTimeMillis() + "";
password = "123456";
}
public User1(String userName, String password) {
// System.out.println("新用户注册");
// registrationTime = System.currentTimeMillis(); //获取系统当前时间(距离1970-1-1 00:00:00的毫秒数)
this.userName = userName;
this.password = password;
}
public String getInfo(){
return "用户名:" + userName + ",密码:" + password + ",注册时间为:" + registrationTime;
}
}
Field,属性赋值过程
总结:
可以给类的非静态的属性(即实例变量)赋值的位置有: ① 默认初始化 ② 显式初始化 或 ⑤ 代码块中初始化 ③ 构造器中初始化 ****************************** ④ 有了对象以后,通过"对象.属性"或"对象.方法"的方法进行赋值 2.执行的先后顺序: ① - ②/⑤ - ③ - ④ 3.(超纲)关于字节码文件中的<init>的简单说明:(通过插件jclasslib bytecode viewer查看) > <init>方法在字节码文件中可以看到。每个<init>方法都对应着一个类的构造器。(类中声明了几个构造器就会有几个<init>) > 编写的代码中的构造器在编译以后就会以<init>方法的方式呈现 > <init>方法内部的代码包含了实例变量的显示赋值、代码块中的赋值和构造器中的代码。 > <init>方法用来初始化当前创建的对象的信息的。 4.给实例变量赋值的位置很多,开发中如何选? > 显示赋值:比较适合于每个对象的属性值相同的场景 > 构造器中赋值:比较适合于每个对象的属性值不相同的场景
相关代码:
public class FieldTest {
public static void main(String[] args) {
Order o1 = new Order();
System.out.println(o1.orderId); //1 ---> 2 ---> 3
}
}
class Order {
/**
* 显式初始化 与 代码块中初始化 可以更换位置
* 此时的执行结果是1
* 反之执行结果是2
* order类的加载有一个系的过程,在初始化之前orderId就已经被默认初始化了,并且默认为0,在后面才开始显示赋值或者代码块赋值
*/
// {
// orderId = 2;
// }
// int orderId = 1;
int orderId = 1;
{
orderId = 2;
}
//构造器会以init的方式呈现的字节码中
//字节码暴露了我们加载的顺序
public Order(){
super();
// orderId = 3;
}
public Order(int orderId){
this.orderId = orderId;
}
//对象调用eat方法之前,类中就已经存在了sleep和eat方法了,所以不会报错
public void eat(){
sleep();
}
public void sleep(){
}
}
字节码文件:
两个构造函数,两个init
面试题1:
/**
* static A
* static B
* I'm A Class
* HelloA
* I'm B Class
* HelloB
*/
class HelloA {
public HelloA() {
System.out.println("HelloA");
}
{
System.out.println("I'm A Class");
}
static{
System.out.println("static A");
}
}
class HelloB extends HelloA {
public HelloB() {
System.out.println("HelloB");
}
{
System.out.println("I'm B Class");
}
static {
System.out.println("static B");
}
}
public class Test01 {
public static void main(String[] args) {
new HelloB();
}
}
面试题2:
public class Test02 {
static int x, y,z;
static {
int x = 5;
x--; //就近原则,操作的是新定义的x
}
static {
x--;
}
public static void method(){
y = z++ + ++z;
}
public static void main(String[] args) {
System.out.println("x = " + x);
z--; //-1
method(); //-1 + 1 = 0 = y z: 1
System.out.println("result:" + (z + y + ++z)); //1 + 0 + 2 = 3
}
}
面试题3:
/**
* Base
* sub:100
* sub
* base:70
*/
class Base {
Base(){
method(100);
}
{
System.out.println("Base");
}
public void method(int i){
System.out.println("base:" + i);
}
}
class Sub extends Base {
Sub(){
super.method(70);
}
{
System.out.println("sub");
}
public void method(int i){
System.out.println("sub:" + i);
}
}
public class Test03 {
public static void main(String[] args) {
Sub s = new Sub();
}
}
关键字:final
总结:
final关键字的使用 1.final的理解:最终的 2.final可以用来修饰的结构:类、方法、变量 3.具体说明: 3.1 final修饰类:表示此类不能被继承。 比如:String、stringBuffer、stringBuilder类 3.2 final修饰方法:表示此方法不能被重写 比如:0bject类中的getClass() 3.3 final修饰变量:既可以修饰成员变量,也可以修饰局部变量。 此时的"变量"其实就变成了"常量",意味着一旦赋值,就不可更改。 3.3.1 final修饰成员变量:有哪些位置可以给成员变量赋值? > 显式赋值 > 代码块中赋值 > 构造器中赋值 3.3.2 final修饰局部变量:一旦赋值就不能修改 > 方法内声明的局部变量: 在调用局部变量前,一定需要赋值。而且一旦赋值,就不可更改 > 方法的形参: 在调用此方法时,给形参进行赋值。而且一旦赋值,就不可更改 4.final与static搭配: 修饰成员变量时,此成员变量称为:全局常量。 比如:Math的PI
相关代码:
public class FinalTest {
public static void main(String[] args) {
E e = new E();
System.out.println(e.MIN_SCORE);
//编译不通过
// e.MIN_SCORE = 1;
E e1 = new E(10);
// e1.LEFT = 11; //不可更改
}
}
class E {
//成员变量
final int MIN_SCORE = 0; //显式赋值
final int MAX_SCORE;
final int LEFT;
// final int RIGHT; //不赋值会报错
{
// MIN_SCORE = 1;
MAX_SCORE = 100; //代码块赋值
}
{
// MAX_SCORE = 1; //编译不通过
}
public E(){
LEFT = 2;
}
public E(int left){
LEFT = left;
}
//报错
// public void setRight(int right){
// RIGHT = right;
// }
}
class F {
public void method(){
final int num = 10;
// num++;//编译报错
System.out.println(num);
}
public void method(final int num){
// num++; //编译报错
System.out.println(num);
}
}
final class A {
}
//报错,不能被继承
//class B extends A {
//
//}
//class subString extends String {}
class C{
public final void method(){}
}
class D extends C{
//不能被重写
// public void method(){
//
// }
}
面试题:
题目1:排错
public class Something {
public int addOne(final int x){
return ++x; //会报错,x已被赋值了
//return x+1;
}
}
题目2:排错
public class Something {
public static void main(String[] args){
Other o = new Other();
new Something().addOne(o);
}
//这个地方final修饰的是o,如果o被重新赋值了的话就会报错
//但是i是一个变量,他是用final修饰的
public void addOne(final Other o){
//o= new Other();
o.i++;
}
}
class Other {
public int i:
}
抽象类与抽象方法
选中这行按alt+enter键,选择实现方法(用于抽象子类重写父类的抽象方法)
弹出要实现的方法确定即可
案例:
Creature
public abstract class Creature { //生物类
public abstract void breath(); //呼吸
}
Person
public abstract class Person extends Creature { //抽象类
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void eat(); //抽象方法
public abstract void sleep(); //抽象方法
}
Student
public class Student extends Person {
String school;
public Student() {
}
public Student(String name, int age, String school) {
super(name, age);
this.school = school;
}
public void eat(){
System.out.println("学生多吃有营养的食物");
}
public void sleep(){
System.out.println("学生要保证充足的睡眠");
}
@Override
public void breath() {
System.out.println("学生应该多呼吸新鲜空气");
}
}
Worker
//public abstract class Worker extends Person{
//
//}
public abstract class Worker extends Person{
@Override
public void eat() {
System.out.println("工人很辛苦,多吃");
}
}
AbstractTest
public class AbstractTest {
public static void main(String[] args) {
//编译不通过
// Person p1 = new Person();
// p1.eat();
Student s1 = new Student();
s1.eat();
//抽象类不能实例化
// Worker w1 = new Worker();
}
}
AbstractTest1
public class AbstractTest1 {
//编译不通过
// private abstract void method(){};
// public static abstract void method(){};
}
抽象类应用
多态的应用:模板方法设计模式(TemplateMethod)
总结:
抽象类与抽象方法 1.案例引入 举例1:Geometric0bject-Circle-Rectangle abstract class Geometric0bject{//几何图形 //求面积(只能考虑提供方法的声明,而没有办法提供方法体。所以,此方法适合声明为抽象方法) //求周长(只能考虑提供方法的声明,而没有办法提供方法体。所以,此方法适合声明为抽象方法) } class Circle extends Geometric0bject { //求面积(必须重写(或实现)父类中的抽象方法) //求周长(必须重写(或实现)父类中的抽象方法) } 举例2:Account-SavingAccount-CheckAcount class Account{ double balance;//余额 //取钱 (声明为抽象方法) //存钱 (声明为抽象方法) } class SavingAccount extends Account{ //储蓄卡 //取钱 (需要重写父类中的抽象方法) //存钱 (需要重写父类中的抽象方法) } class CheckAccount extends Account{ //信用卡 //取钱 (需要重写父类中的抽象方法) //存钱 (需要重写父类中的抽象方法) } //...... 2.abstract的概念:抽象的 3.abstract可以用来修饰:类、方法 4.具体的使用: 4.1 abstract修饰类: > 此类称为抽象类 > 抽象类不能实例化。 > 抽象类中是包含构造器的,因为子类对象实例化时,需要直接或间接的调用到父类的构造器。 > 抽象类中可以没有抽象方法。反之,抽象方法所在的类,一定是抽象类, (因为需要确保这个方法不能被调用,而方法是需要通过对象来调用,这样干脆让类为抽象类,不能实例化) 4.2 abstract修饰方法 > 此方法即为抽象方法 > 抽象方法只有方法的声明,没有方法体。 > 抽象方法其功能是确定的(通过方法的声明即可确定),只是不知道如何具体实现(体现为没有方法体) > 子类必须重写父类中的所有的抽象方法之后,方可实例化。否则,此子类仍然是一个抽象类 5.abstract不能使用的场景 5.1 abstract 不能修饰哪些结构? 属性、构造器、代码块等。 5.2 abstract 不能与哪些关键字共用? (自洽) 不能用abstract修饰私有方法、静态方法、final的方法、final的类 > 私有方法不能重写 > 避免静态方法使用类进行调用 > final的方法不能被重写 > final修饰的类不能有子类
练习:
编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个 Employee对象的生日,则将该雇员的工资增加100元。 实验说明: (1)定义一个Employee类,该类包含: private成员变量name,number,birthday,其中birthday 为MyDate类的对象;提供必要的构造器 abstract方法earnings(); toString()方法输出对象的name,number和birthday。 (2)MyDate类包含: private成员变量year,month,day ;提供必要的构造器 toDateString()方法返回日期对应的字符串:xxxx年xx月xx日 (3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处 理。该类包括:private成员变量monthlySalary; 提供必要的构造器; 实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输 出员工类型信息及员工的name,number,birthday。比如:SalariedEmployee[name = '', number = '', birthday = ''] (4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的 员工处理。该类包括: private成员变量wage和hour; 提供必要的构造器; 实现父类的抽象方法earnings(),该方法返回wage*hour值; toString()方法输出员工类型信息及员工的name,number,birthday。比如:HourlyEmployee[name = '', number = '', 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);TemplateMethodTest public class TemplateMethodTest { public static void main(String[] args) { BankTemplateMethod btm = new DrawMonney(); btm.process(); BankTemplateMethod btm2 = new ManageMonney(); 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 DrawMonney extends BankTemplateMethod { public void transact(){ System.out.println("我要取款!!!"); } } class ManageMonney extends BankTemplateMethod { public void transact(){ System.out.println("我要理财!我这里有2000万美元!!"); } } 或练习 public class TemplateTest { public static void main(String[] args) { PrintPrimeNumber p = new PrintPrimeNumber(); p.spendTime(); } } abstract class Template { //计算某段代码的执行,需要花费的时间 public void spendTime(){ long start = System.currentTimeMillis(); code(); long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - start)); } public abstract void code(); } class PrintPrimeNumber extends Template { public void code(){ for(int i = 2; i <= 100000; 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); } } } }
接口(interface)
相关代码:
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flyable.MIN_SPEED);
System.out.println(Flyable.MAX_SPEED);
//编译报错
// Flyable.MAX_SPEED = 7800;
Bullet b1 = new Bullet();
b1.fly();
b1.attack();
//接口的多态性
Flyable f1 = new Bullet();
f1.fly(); //编译的时候是接口当中的方法,但是运行的时候是实现类当中的方法
}
}
interface Flyable { //接口
//全局常量
public static final int MIN_SPEED = 0;
// 可以省略public static final
int MAX_SPEED = 7900;
//方法可以省略public abstract声明
// public abstract void fly();
void fly();
}
interface Attackable { //接口
public abstract void attack();
}
abstract class Plane implements Flyable,Attackable {
}
class Bullet implements Flyable {
public void fly() {
System.out.println("让子弹飞一会儿");
}
public void attack(){
System.out.println("子弹可以击穿身体");
}
}
//测试接口的继承关系
interface AA {
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{ //接口可以多继承
}
class DD implements CC {
public void method1(){
}
public void method2(){
}
}
练习题:
设备连接
/**
* 设备连接成功...
* 打印机开始工作
* 数据传输的细节操作...
* 打印机结束工作
* 设备连接成功...
* 照相机开始工作
* 数据传输的细节操作...
* 照相机结束工作
* 设备连接成功...
* U盘开始工作
* 数据传输的细节操作...
* U盘开始结束工作
* 设备连接成功...
* 扫描仪开始工作
* 数据传输的细节操作...
* 扫描仪结束工作
*/
public class USBTest {
public static void main(String[] args) {
//1.创建接口实现类的对象
Computer computer = new Computer();
Printer printer = new Printer();
computer.transferDate(printer);
//2.创建接口实现类的匿名对象
computer.transferDate(new Camera());
//3.创建接口匿名实现类的对象
USB usb1 = new USB(){
public void start(){
System.out.println("U盘开始工作");
}
public void stop(){
System.out.println("U盘开始结束工作");
}
};
computer.transferDate(usb1);
//4.创建接口匿名实现类的匿名对象
computer.transferDate(new USB(){
public void start(){
System.out.println("扫描仪开始工作");
}
public void stop(){
System.out.println("扫描仪结束工作");
}
});
}
}
class Computer {
public void transferDate(USB usb){
System.out.println("设备连接成功...");
usb.start();
System.out.println("数据传输的细节操作...");
usb.stop();
}
}
class Camera implements USB {
@Override
public void start() {
System.out.println("照相机开始工作");
}
@Override
public void stop() {
System.out.println("照相机结束工作");
}
}
class Printer implements USB {
@Override
public void start() {
System.out.println("打印机开始工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
interface USB {
//声明常量
//USB的长、宽、高...
//方法
public abstract void start();
void stop();
}
练习2:
1、声明接口Eatable,包含抽象方法public abstract void eat(); 2、声明实现类中国人Chinese,重写抽象方法,打印用筷子吃饭 3、声明实现类美国人American,重写抽象方法,打印用刀叉吃饭 4、声明实现类印度人Indian,重写抽象方法,打印用手抓饭 5、声明测试类EatableTest,创建Eatable数组,存储各国人对象,并遍历数组,调用eat()方法
Chinese
public class Chinese implements Eatable {
public void eat() {
System.out.println("中国人使用筷子吃饭");
}
}
American
public class American implements Eatable {
@Override
public void eat() {
System.out.println("美国人使用刀叉吃饭");
}
}
Indian
public class Indian implements Eatable{
@Override
public void eat() {
System.out.println("印度人使用手抓饭");
}
}
public interface Eatable {
void eat();
}
EatableTest
public class EatableTest {
public static void main(String[] args) {
Eatable[] eatables = new Eatable[3];
eatables[0] = new Chinese(); //多态性
eatables[1] = new American();
eatables[2] = new Indian();
/**
* 中国人使用筷子吃饭
* 美国人使用刀叉吃饭
* 印度人使用手抓饭
*/
for (int i = 0; i < eatables.length; i++) {
eatables[i].eat();
}
}
}
练习题3:
定义一个接口用来实现两个对象的比较。 interface CompareObject{ //若返回值是0,代表相等;若为正数,代表当前对象大;负数代表当前对象小 public int compareTo(Object o); } 定义一个Circle类,声明radius属性,提供getter和setter方法 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。 在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。 定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小。 拓展:参照上述做法定义矩形类Rectangle和ComparableRectangle类,在ComparableRectangle类 中给出compareTo方法的实现,比较两个炬形的面积大小。
CompareObject
public interface CompareObject {
//若返回值是 0,代表相等,若为正数,代表当前对象大;负数代表当前对象小
public int compareTo(Object o);
}
Circle
public class Circle {
private double radius; //半径
public Circle() {
}
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public String toString() {
return "Circle{" +
"radius=" + radius +
'}';
}
}
ComparableCircle
public class ComparableCircle extends Circle implements CompareObject {
public ComparableCircle() {
}
public ComparableCircle(double radius) {
super(radius);
}
//根据对象半径的大小,比较对象的大小
@Override
public int compareTo(Object o) {
if(this == o) {
return 0;
}
if(o instanceof ComparableCircle) {
ComparableCircle c = (ComparableCircle)o;
// 错误的
// return (int)(this.getRadius() - c.getRadius());
//正确的写法1:
// if(this.getRadius() > c.getRadius()) {
// return 1;
// }else if(this.getRadius() < c.getRadius()){
// return -1;
// }else {
// return 0;
// }
//正确的写法2:
return Double.compare(this.getRadius(), c.getRadius());
}else {
return 2; //如果输入的类型不匹配,则返回2
// throw new RuntimeException("输入的类型不匹配");
}
}
}
InterfaceTest
public class InterfaceTest {
public static void main(String[] args) {
ComparableCircle c1 = new ComparableCircle(2.3);
ComparableCircle c2 = new ComparableCircle(5.3);
int compareValue = c1.compareTo(c2);
if(compareValue > 0){
System.out.println("c1对象大");
}else if(compareValue < 0){
System.out.println("c2对象大");
}else{
System.out.println("c1和c2一样大");
}
}
}
接口的应用:代理模式(Proxy)
总结:
接口的使用 1.接口的理解:接口的本质是契约、标准、规范,就像我们的法律一样。制好后大家都要遵守 2.定义接口的关键字:interface 3.接口内部结构的说明: > 可以声明: 属性:必须使用public static final修饰 方法:jdk8之前:声明抽象方法,修饰为public abstract jdk8:声明静态方法、默认方法 jdk9:声明私有方法 > 不可以声明: 构造器、代码块等 4.接口与类的关系:实现关系 5.格式: class A extends SuperA implements B,C{} A相较于SuperA来讲,叫做子类 A相较于B,C来讲,叫做实现类。 6.满足此关系之后,说明: > 类可以实现多个接口。 > 类针对于接口的多实现,一定程度上就弥补了类的单继承的局限性。 > 类必须将实现的接口中的所有的抽象方法都重写(或实现),方可实例化。否则,此实现类必须声明为抽象类。 7.接口与接口的关系: 继承关系,且可以多继承 8.接口的多态性: 接口名 变量名 = new 实现类对象; // 父类 变量名 = new 子类对象; 类的多态性 9.面试题:区分抽象类和接口 > 共性:都可以声明抽象方法 都不能实例化 > 不同:① 抽象类一定有构造器。接口没有构造器 ② 类与类之间继承关系,类与接口之间是实现关系,接口与接口之间是多继承关系
相关代码 :
CompareA
public interface CompareA {
//属性:声明为public static final
//方法:jdk8之前,只能声明抽象方法
//方法:jdk8中:静态方法
public static void method1(){
System.out.println("CompareA:北京");
}
//方法:jdk8中:默认方法
// public void method3(); //这种写法是抽象方法,不是抽象方法就需要抽象体
public default void method2(){
System.out.println("CompareA:上海");
};
public default void method3(){
System.out.println("CompareA:广州");
};
public default void method4(){
System.out.println("CompareA:深圳");
};
//jdk9新特性:定义私有方法
private void method5(){
System.out.println("我是接口中定义的私有方法");
}
}
CompareB
public interface CompareB {
public default void method3(){
System.out.println("CompareB: 广州");
}
}
SuperClass
public class SuperClass {
public void method4() {
System.out.println("SuperClass:深圳");
}
}
SubClassTest
public class SubClassTest {
public static void main(String[] args) {
//知识点1:接口中声明的静态方法只能被接口来调用,不能使用期实现类进行调用
CompareA.method1();
// SubClass.method1(); //编译不通过
//默认方法涉及到造对象,重写之后调用的是重写之后的方法
//知识点2:接口中声明的默认方法可以被类继承,实现类在没有重写此方法的情况下,默认调用接口中声明的
//默认方法,如果实现类重写了此方法,则调用的是自己重写的方法
SubClass s1 = new SubClass();
s1.method2();
//知识点3:类实现了两个接口,而两个接口中定义了同名同参数的默认方法,则实现类在没有重写此两个接口
//默认方法的情况下,或报错,--->接口冲突
//要求:此时实现类必须要重写接口中定义的同名同参数的方法
s1.method3();
//知识点4:子类(或实现类)继承了父类并实现了接口,父类和接口中声明了同名同参数的方法,(其中,接口中的方法
//是默认方法)。默认情况下,子类(或实现类)在没有重写此方法的情况下,调用的是父类中的方法。---> 类优先原则
/**
* CompareA:北京
* SubClass:上海
* SubClass:广州
* SuperClass:深圳 ---> SubClass:深圳(子类重写之后)
*/
s1.method4();
}
}
SubClass
public class SubClass extends SuperClass implements CompareA, CompareB {
@Override
public void method2() {
System.out.println("SubClass:上海");
}
@Override
public void method3() {
System.out.println("SubClass:广州");
}
public void method4() {
System.out.println("SubClass:深圳");
}
public void method(){
//知识点5:如何在子类(或实现类)中调用父类或接口中重写的方法
method4(); //调用自己类中的方法
super.method4();
method3(); //调用自己类中的方法
// CompareA.method1(); //静态方法可以这样调用,不是静态的方法按照下面的方式调用
CompareA.super.method3(); //调用接口CompareA中的默认方法
CompareB.super.method3(); //调用接口CompareB中的默认方法
}
}
类的成员之五:内部类
内部类生成的文件通过 $内部类名 来表示
代码示例:
public class OuterClassTest {
public static void main(String[] args) {
//1.创建Person的静态的成员内部类的实例
Person.Dog dog = new Person.Dog();
dog.eat();
//2.创建Person的非静态的成员内部类的实例
// Person.Bird bird = new Person.Bird(); //报错
Person p1 = new Person();
Person.Bird bird = p1.new Bird(); //正确的
bird.eat();
bird.show("黄鹂");
bird.show1();
}
}
class Person { //外部类
String name = "Tom";
int age = 1;
//静态的成员内部类
static class Dog{
public void eat() {
System.out.println("狗吃骨头");
}
}
//非静态的成员内部类
class Bird{
String name = "啄木鸟";
public void eat(){
System.out.println("鸟吃虫子");
}
public void show(String name){
System.out.println("age = " + age); //age = 1 省略了Person.this
System.out.println("name = " + name); //name = 黄鹂
System.out.println("name = " + this.name); //name = 啄木鸟
System.out.println("name = " + Person.this.name); //name = Tom
}
public void show1(){
eat(); //鸟吃虫子
this.eat(); //鸟吃虫子
Person.this.eat(); //人吃饭
}
}
public void eat(){
System.out.println("人吃饭");
}
public void method(){
//局部内部类
class innerClass1{
}
}
public Person(){
//局部内部类
class innerClass1{
}
}
{
//局部内部类
class innerClass1{
}
}
}
代码示例2:
public class OuterClassTest1 {
//说明:局部内部类的使用
public void method1(){
//局部内部类
class A{
//可以声明属性、方法等
}
}
//开发中的场景
public Comparable getInstance(){
//提供了实现了Comparable接口的类
//方式1:提供了接口的实现类的对象
// class MyComparable implements Comparable{
//
// @Override
// public int compareTo(Object o) {
// return 0;
// }
// }
// MyComparable m = new MyComparable();
// return m;
//方式1:提供了接口的实现类的匿名对象
// class MyComparable implements Comparable{
//
// @Override
// public int compareTo(Object o) {
// return 0;
// }
// }
// return new MyComparable();
//方式2:提供了接口的匿名实现类的对象
//后面的大括号表示实现类
// Comparable c = new Comparable() {
// @Override
// public int compareTo(Object o) {
// return 0;
// }
// };
// return c;
//方式3:提供了接口的匿名实现类的匿名对象
return new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
代码示例3:
public class OuterClassTest2 {
public static void main(String[] args) {
SubA a = new SubA();
a.method();
//举例1:提供了接口匿名实现类的对象
A a1 = new A(){
public void method() {
System.out.println("匿名实现类重写的方法method()");
}
};
a1.method();
//举例2:提供了接口匿名实现类的匿名对象
new A(){
public void method() {
System.out.println("匿名实现类重写的方法method()");
}
}.method();
//举例3:
SubB s1 = new SubB();
s1.method1();
//举例4:提供了继承于抽象类的匿名子类的对象
B b = new B() {
@Override
public void method1() {
System.out.println("继承于抽象类的子类调用的方法");
}
};
b.method1();
System.out.println(b.getClass()); //class com.atguigu09.inner.OuterClassTest2$3
System.out.println(b.getClass().getSuperclass()); //class com.atguigu09.inner.B
//举例5:
new B(){
@Override
public void method1() {
System.out.println("继承于抽象类的子类调用的方法1");
}
}.method1();
//举例6:
C c = new C();
c.method2();
//举例7:提供了一个继承于C的匿名子类的对象
// C c1 = new C(){};
C c1 = new C(){
public void method2() {
System.out.println("SubC");
}
};
c1.method2();
// System.out.println(c.getClass());//class com.atguigu09.inner.C
// System.out.println(c1.getClass().getSuperclass());//class com.atguigu09.inner.C
}
}
interface A{
public void method();
}
class SubA implements A{
public void method(){
System.out.println("SubA");
}
}
abstract class B{
public abstract void method1();
}
class SubB extends B{
public void method1(){
System.out.println("SubB");
}
}
class C{
public void method2(){
System.out.println("C");
}
}
练习1:
编写一个匿名内部类,它继承Object,并在匿名内部类中,声明一个方法public void test()打印尚硅谷。 请编写代码调用这个方法。
public class ObjectTest {
public static void main(String[] args) {
// SubObject sub1 = new SubObject();
// sub1.test();
//提供一个继承于Object的匿名子类的匿名对象
new Object(){
public void test(){
System.out.println("尚硅谷");
}
}.test();
}
}
class SubObject extends Object{
public void test(){
System.out.println("尚硅谷");
}
}
native关键字的理解:
枚举类enum
单例模式当中有饿汉式与懒汉式
总结:
枚举类的使用 1.枚举类的理解:枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。 2.举例: - 星期: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(取消) - 线程状态:创建、就绪、运行、阻塞、死亡 3.开发中的建议: > 开发中,如果针对于某个类,其实例是确定个数的,则推荐将此类声明为枚举类 > 如果枚举类的实例只有一个,则可以看做是单例的实现方式。 4.JDK5.0 之前如何自定义枚举类(了解) 见代码 5.JDK5.0中使用enum定义枚举类 见代码 6.Enum中的常用方法: 6.1 使用enum关键字定义的枚举类,默认其父类是java.lang.Enum类 使用enum关键字定义的枚举类,不要再显示的定义其父类。否则报错 6.2 熟悉Enum类中常用的方法 string toString():默认返回的是常量名(对象名),可以继续手动重写该方法! (关注)static 枚举类型[] valves():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法 (关注)static 枚举类型 value0f(String name):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的"名字"。如不是,会有运行时异常:IllegalArgumentException。 String name():得到当前枚举常量的名称。建议优先使用toString()。 int ordinal():返回当前枚举常量的次序号,默认从0开始 7.枚举类实现接口的操作 情况1:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是同一个方法。 情况2:让枚举类的每一个对象重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是不同的实现的方法。
相关代码1:
public class SeasonTest {
public static void main(String[] args) {
// Season.AUTUMN = null; //报错
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER.getSeasonName());
System.out.println(Season.SUMMER.getSeasonDesc());
}
}
//jdk5.0之前定义的枚举方式
class Season{
//2.声明当前类的对象的实例变量,使用private final修饰
private final String seasonName; //季节的名称
private final String seasonDesc; //季节的描述
//1.私有化类的构造器
private Season(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//3.提供实例变量的get方法
public String getSeasonName(){
return seasonName;
}
public String getSeasonDesc(){
return seasonDesc;
}
//4.创建当前类的实例,使用public static final修饰
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("冬天", "白雪皑皑");
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
相关代码2:
public class SeasonTest1 {
public static void main(String[] args) {
// System.out.println(Season1.SPRING.getClass()); //class com.atguigu10._enum.Season1
// System.out.println(Season1.SPRING.getClass().getSuperclass()); //class java.lang.Enum
// System.out.println(Season1.SPRING.getClass().getSuperclass().getSuperclass()); //class java.lang.Object
//测试方法
//1.toString()
System.out.println(Season1.SPRING); //SPRING(toString()重写之前)
//2.name()
System.out.println(Season1.SPRING.name()); //SPRING
//3.values()
Season1[] values = Season1.values();
/**
* Season1{seasonName='春天', seasonDesc='春暖花开'}
* Season1{seasonName='夏天', seasonDesc='夏日炎炎'}
* Season1{seasonName='秋天', seasonDesc='秋高气爽'}
* Season1{seasonName='冬天', seasonDesc='白雪皑皑'}
*/
for(int i=0; i<values.length; i++){
System.out.println(values[i]);
}
//4.valueOf(String objName):再返回当前枚举类中名称为objName的枚举对象
//如果枚举类不存在objName名称的对象,则报错
String objName = "WINTER";
// objName = "WINTER1"; //报错IllegalArgumentException
Season1 season1 = Season1.valueOf(objName);
System.out.println(season1);
//5.ordinal()
System.out.println(Season1.AUTUMN.ordinal()); //2
//通过枚举类的对象调用重写后的接口中的方法
Season1.AUTUMN.show();
}
}
interface Info{
void show();
}
//jdk5.0中使用enum关键字定义枚举类
enum Season1 implements Info{
//1.必须在枚举类的开头声明多个对象,对象之间使用,隔开
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "白雪皑皑");
//2.声明当前类的对象的实例变量,使用private final修饰
private final String seasonName; //季节的名称
private final String seasonDesc; //季节的描述
//3.私有化类的构造器
private Season1(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//3.提供实例变量的get方法
public String getSeasonName(){
return seasonName;
}
public String getSeasonDesc(){
return seasonDesc;
}
@Override
public String toString() {
return "Season1{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
@Override
public void show() {
System.out.println("这是一个季节");
}
}
改进2
public class SeasonTest2 {
public static void main(String[] args) {
Season2[] values = Season2.values();
for(int i = 0; i < values.length; i++){
// System.out.println(values[i]);
values[i].show();
}
}
}
interface Info1{
void show();
}
//jdk5.0中使用enum关键字定义枚举类
enum Season2 implements Info1{
//1.必须在枚举类的开头声明多个对象,对象之间使用,隔开
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("大约在冬季");
}
};
//2.声明当前类的对象的实例变量,使用private final修饰
private final String seasonName; //季节的名称
private final String seasonDesc; //季节的描述
//3.私有化类的构造器
private Season2(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//3.提供实例变量的get方法
public String getSeasonName(){
return seasonName;
}
public String getSeasonDesc(){
return seasonDesc;
}
@Override
public String toString() {
return "Season1{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
就职状态代码:
Employee
public class Employee {
private String name;
private int age;
private Status status;
public Employee() {
}
public Employee(String name, int age, Status status) {
this.name = name;
this.age = age;
this.status = status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", status=" + status +
'}';
}
}
枚举:
public enum Status {
BUSY,FREE,VOCATION,DIMISSION;
}
EmployeeTest
public class EmployeeTest {
public static void main(String[] args) {
Employee e1 = new Employee("Tom", 21, Status.BUSY);
System.out.println(e1); //Employee{name='Tom', age=21, status=BUSY}
}
}
案例:使用枚举类实现单例模式
public class BnakTest1 {
public static void main(String[] args) {
// Bank1.instance = null; //final可以用但不能修改
System.out.println(GirlFriend.XIAL_LI); //XIAL_LI
}
}
//jdk5.0之前的使用枚举类定义单例模式
class Bank1 {
//饿汉式
// private Bank1(){}
// private static Bank1 instance = new Bank1();
// public static Bank1 getInstance(){
// return instance;
// }
//枚举类没有通过方法调用,而是直接通过类来调用
private Bank1(){}
private static final Bank1 instance = new Bank1();
}
//jdk5.0使用enum关键字定义枚举类的方式定义单例模式
enum Bank2{
CPB;
}
enum GirlFriend{
// public static final GirlFriend XIAL_LI = new GirlFriend(20); //之前写法
XIAL_LI(20);
private final int age;
private GirlFriend(int age){
this.age = age;
}
}
案例:
案例拓展:颜色枚举类(使用enum声明) (1)声明颜色枚举类Color: - 声明final修饰的int类型的属性red,green,blue - 声明final修饰的String类型的属性description - 声明有参构造器Color(int red,int green,int blue,String description) - 创建7个常量对象:红、橙、黄、绿、青、蓝、紫, - 重写toString方法,例如:RED(255,0,0)->红色 (2)在测试类中,使用枚举类,获取绿色对象,并打印对象。 提示: - 7个常量对象的RGB值如下: 红:(255,0,0) 橙:(255,128,0) 黄:(255,255,0) 绿:(0,255,0) 青:(0,255,255) 蓝:(0,0,255) 紫:(128,0,255) 7个常量对象名如下 RED, ORANGE,YELLOW, GREEN,CYAN, BLUE,PURPLEpublic class ColorTest { public static void main(String[] args) { System.out.println(Color.BLUE); } } enum Color { RED(255,0,0,"红色"), ORANGE(255,128,0,"橙色"), YELLOW(255,255,0,"黄色"), GREEN(0,255,0,"绿色"), CYAN(0,255,255,"青色"), BLUE(0,0,255,"蓝色"), PURPLE(128,0,255,"紫色"); private final int red; private final int green; private final int blue; private final String description; //颜色的描述 Color(int red, int green, int blue, String description) { this.red = red; this.green = green; this.blue = blue; this.description = description; } public int getRed() { return red; } public int getGreen() { return green; } public int getBlue() { return blue; } public String getDescription() { return description; } @Override public String toString() { // return name()+ "(" + red + "," + green + "," + blue + ")" + "--->" + description; return super.toString()+ "(" + red + "," + green + "," + blue + ")" + "--->" + description; } }
annotation注解的使用
`@SuppressWarnings`:抑制编译器警告
移动到元素上,选择更多操作
结果如下: @SuppressWarnings("unused") int num = 10;
JUnit单元测试
怎么添加单元测试包,通过+号选择库(library)
务必选择编译
就可以为当前模块(module)添加单元测试(junit)
也可以通过选中当前@Test这个测试类,按住alt+enter回车键,在有网的情况下下载包
默认情况下,单元测试方法中使用Scanner失效。如何解决?
在单元测试里面如果没有做什么配置,在这个地方输入什么都无法显示
如果不想是只读的,输入的数值可以显示出来,在help里面选择编辑自定义虚拟机选项
在这个文件中将这一句话加上:-Deditable.java.test.console=true
之后在文件地方选择使缓存失效
点击重启,或者直接关闭idea,重新打开也可
此时再输入数值就可以正常显示了
结果显示:
如何将将单元测试设置成一个模板?
使用模板,在光标出输入需要输入的值
总结和相关代码:
JUnit单元测试的使用 1.需要导入的jar包: junit-4.12.jar hamcrest-core-1.3.jar 2.导入步骤 见课件 3.创建单元测试类,进行测试 见代码 4.(重点关注)要想能正确的编写单元测试方法,需要满足: - 所在的类必须是public的,非抽象的,包含唯一的无参构造器。 - @Test标记的方法本身必须是public,非抽象的,非静态的,void无返回值,()无参数的。 5.默认情况下,单元测试方法中使用Scanner失效。如何解决? 6.大家可以将单元测试设置成一个模板?public class JunitTest { //单元测试类 public static void main(String[] args) { //静态方法调用非静态方法需要进行测试 JunitTest test = new JunitTest(); System.out.println(test.number); test.method(); } int number = 10; @Test public void test1() { //单元测试方法 System.out.println("Hello"); } @Test public void test2() { System.out.println("Hello1"); System.out.println("number = " + number); method(); int num = showInfo("China"); System.out.println(num); } public void method() { System.out.println("method()......"); } public int showInfo(String info){ System.out.println(info); return 10; } @Test public void test3() { Scanner sc = new Scanner(System.in); System.out.println("请输入一个数值"); int num = sc.nextInt(); System.out.println(num); for(int i = 1; i <= num; i++){ System.out.println(i); } } @Test public void test4(){ System.out.println("Hello4"); } @Test public void test5(){ System.out.println("Hello5"); } }
包装类(Wrapper)的使用
基本类型、包装类与String类间的转换
字节码文件可以体现出自动装箱与自动拆箱的过程
包装类缓存对象
面试题1:
public class InterviewTest1 {
public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); // false
//底层都会调用Integer的valueOf()
//这个地方取得值是里面已经new好的Integer数组里面的对象,cache数组中每一个位置都是一个new Integer()对象,
//所以他们的地址值都是相同的,都是取的cache数组中的第129位
Integer m = 1; //自动装箱
Integer n = 1;
System.out.println(m == n); //true
//根据valueOf的源码可知,这个地方的128不在[-128,127]范围内,所以是new Integer(i)的一个对象出来
Integer x = 128;
Integer y = 128;
System.out.println(x == y); //false
}
}
cache数组长度是256,循环,cache数组[第一位是-128,第二位是-127.....一直到127]为止
用自动装箱的方式,在这个范围内用==判断的全都是true,出了这个范围是false
面试题2:
public class InterviewTest2 {
public static void main(String[] args) {
//题目1:
int i = 10;
double d = 10.2;
System.out.println(i == d); //false
//题目2:
Integer i1 = 10;
Double d1 = 10.2;
// System.out.println(i1 == d1); //报错,==可以比较引用类型的地址,但是需要时同一类型
//题目3:会进行拆箱,m就会转成int类型的,装箱会成为不同引用类型,会报错
Integer m = 1000; //1000先装箱,比较时拆箱
double n = 1000;
System.out.println(m == n); //true
//题目4:
Integer x = 1000;
int y = 1000;
System.out.println(x == y); // true
}
}
int(i)类型转换成(to)成double类型(d) i2d
面试题3:
public class InterviewTest3 {
public static void main(String[] args) {
//这个地方的比较有一个类型兼容的问题,所以输出结果是1.0
Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1); //1.0
Object o2;
if(true)
o2 = new Integer(1);
else
o2 = new Double(2.0);
System.out.println(o2); //1
}
}
总结:
包装类的使用 1.为什么要使用包装类? 为了使得基本数据类型的变量具备引用数据类型变量的相关特征(比如:封装性、继承性、多态性),我们给各个基本数据 类型的变量都提供了对应的包装类。 2.(掌握)基本数据类型对应的包装类类型 byte -> Byte short -> Short int -> Integer long -> Long float -> Float double ->Double char -> Character boolean -> Boolean 3.掌握基本数据类型 与 包装类之间的转换。 3.1 为什么需要转换 > 一方面,在有些场景下,需要使用基本数据类型对应的包装类的对象。此时就需要将基本数据类型的变量转换为 包装类的对象。比如:ArrayList的add(Object obj);Object类的equals(Object obj) > 对于包装类来讲,既然我们使用的是对象,那么对象是不能进行+ - * /等运算的。为了能够进行这些运算,就 需要将包装类的对象转换为基本数据类型的变量。 3.2 如何转换: (装箱)基本数据类型 ---> 包装类: ① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxxxx) (拆箱)包装类 ---> 基本数据类型: 调用包装类的xxxValue() 注意:原来使用基本数据类型变量的位置,改成包装类以后,对于成员变量来说,其默认值变化了! jdk5.0新特性:自动装箱、自动拆箱。 4.String 与 基本数据类型、包装类之间的转换。 基本数据类型、包装类---> String类型: ① String类型:调用String的重载的静态方法value0f(xxxxx); ② 基本数据类型的变量 + "" String类型 ---> 基本数据类型、包装类:调用包装类的静态方法:parseXxx()
相关代码:代码练习
public class WrapperTest {
/**
* 基本数据类型 ---> 包装类: ① 使用包装类的构造器 ② (建议)调用包装类的value0f(xxx xx)
* 包装类 ---> 基本数据类型: 调用包装类的xxxValue()
* jdk5.0新特性:自动装箱、自动拆箱。
*/
@Test
public void test4(){
//自动装箱: 基本数据类型--->包装类
int i1 = 10;
Integer ii1 = i1;//自动装箱
System.out.println(ii1.toString());
Integer ii2 = i1 + 1; //自动装箱
Boolean bb1 = true;//自动装箱
Float f1 = 12.3F;//自动装箱
//自动拆箱:包装类---> 基本数据类型
int i2 = ii1; //自动拆箱
boolean b1 = bb1; //自动拆箱
}
@Test
public void test3(){
Account account = new Account();
System.out.println(account.isFlag1); //false
System.out.println(account.isFlag2); //null
System.out.println(account.balance1); //0.0
System.out.println(account.balance2); //null
}
@Test
public void test2(){
Integer ii1 = new Integer(10);
int i1 = ii1.intValue();
i1 = i1 + 1;
Float ff1 = new Float(12.3F);
float f1 = ff1.floatValue();
Boolean bb1 = Boolean.valueOf(true);
Boolean b1 = bb1.booleanValue();
}
@Test
//基本数据类型 ---> 包装类: ① 使用包装类的构造器 ② (建议)调用包装类的value0f(xxx xx)
public void test1(){
int i1 = 10;
Integer ii1 = new Integer(i1);
System.out.println(ii1.toString()); //10
float f1 = 12.3F;
f1 = 32.2f;
Float ff1 = new Float(f1);
System.out.println(ff1.toString()); //32.2
String s1 = "32.1";
Float ff2 = new Float(s1);
// s1 = "abc";
// Float ff3 = new Float(s1); //报异常:NumberFormatException
boolean b1 = true;
Boolean bb1 = new Boolean(b1);
System.out.println(bb1);//true
//Boolean这块只要长的跟true不一样就是false了
String s2 = "false";
s2 = "False123";
s2 = "TrUe"; //忽略大小写
Boolean bb2 = new Boolean(s2);
System.out.println(bb2); //false --> true
//推荐使用
int i2 = 10;
Integer ii2 = Integer.valueOf(i2);
Boolean b2 = Boolean.valueOf(true);
Float f2 = Float.valueOf(12.3F);
}
}
class Account{
boolean isFlag1;
Boolean isFlag2; //null 包装类
double balance1; //0.0
Double balance2; //null 包装类 0.0可以理解成这个对象被初始化了
}
相关代码2:
public class WrapperTest1 {
/**
* 基本数据类型、包装类---> ① String类型:调用String的重载的静态方法value0f(xxxxx); ② 基本数据类型的变量 + ""
* String类型 ---> 基本数据类型、包装类:调用包装类的静态方法:parseXxx()
*/
@Test
public void test2(){
String s1 = "123";
int i1 = Integer.parseInt(s1);
System.out.println(i1 + 10);
String s2 = "true";
boolean b1 = Boolean.parseBoolean(s2);
//特别的
// String s3 = "123a";
// int i2 = Integer.parseInt(s3); //报错NumberFormatException
}
@Test
public void test1(){
//方式1:① String类型:调用String的重载的静态方法value0f(xxxxx); ② 基本数据类型的变量 + ""
int i1 = 10;
String str1 = String.valueOf(i1);
System.out.println(str1); //"10"
boolean b1 = true;
Boolean b2 = b1;
String str2 = String.valueOf(b1);
String str3 = String.valueOf(b2);
//方式2:基本数据类型的变量 + ""
String str4 = i1 + "";
String str5 = b1 + "";
}
}
练习题:
利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级。 提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动 1、创建Vector对象:Vector v=new Vector(); 2、给向量添加元素:v.addElement(Object obj);//obj必须是对象 3、取出向量中的元素:Object obj=v.elementAt(0); 注意第一个元素的下标是0,返回值是0bject类型的。 4、计算向量的长度:v.size(); 5、若与最高分相差10分内:A等;20分内:B等;30分内:C等;其它:D等
package com.atguigu12.wrapper.exer;
import java.util.Scanner;
import java.util.Vector;
/**
* ClassName: ScoreTest
* Package: com.atguigu12.wrapper.exer
* Description:
*
* @Author: shkstart
* @Create 2025-03-25 23:46
* @Version: 1.0
*/
public class ScoreTest {
public static void main(String[] args) {
//1.创建Vector对象:Vector v= new Vector();
Vector v= new Vector();
Scanner scanner = new Scanner(System.in);
int maxScore = 0; //记录最高分
//2.从键盘中获取学生成绩,存放到v中(以负数代表输入结果)
while(true){
System.out.print("请输入学生成绩(以负数代表输入结束):");
int intScore = scanner.nextInt();
if(intScore < 0){
break;
}
// //装箱:int ---> Integer对象
// Integer score = Integer.valueOf(intScore);
// //添加学生成绩到容器v中
// v.addElement(score);
//jdk5.0之后,自动装箱
v.addElement(intScore);
//3.获取学生成绩的最大值
if(maxScore < intScore){
maxScore = intScore;
}
}
System.out.println("最高分:" + maxScore);
//4.依次获取v中的每个学生成绩,与最高分进行比较,获取学生等级,进行输出
for(int i = 0; i < v.size(); i++){
Object objScore = v.elementAt(i);
//方式1:
// Integer integerScore = (Integer)objScore;
// //拆箱
// int score = integerScore.intValue();
//方式2:自动拆箱
int score = (Integer)objScore;
char grade = ' ';
if(maxScore - score <= 10){
grade = 'A';
}else if(maxScore - score <= 20){
grade = 'B';
}else if(maxScore - score <= 30){
grade = 'C';
}else {
grade = 'D';
}
System.out.println("student " + i + " score: " + score + " grade: " + grade);
}
scanner.close();
}
}
ctrl+fn+f12或ctrl+f12在当前文件进行搜索,搜索内容可以直接输入
IDEA断点调试
Person p1 = new Person(3); System.out.println(p1.toString());toString()没有重写之前打印的是地址,对于引用数据类型来说,重写之后,打印的是重写之后的方法
总结:
IDEA中调试程序 1.为什么需要Debug? 编好的程序在执行过程中如果出现错误,该如何查找或定位错误呢?简单的代码直接就可以看出来, 但如果代码比较复杂,就需要借助程序调试工具(Debug)来查找错误了。 2. Debug的步骤 1、添加断点 2、启动调试 3、单步执行 4、观察变量和执行流程,找到并解决问题
Description:演示1:行断点,测试debug各个常见操作按钮
Description: 演示2: 方法断点
Description:演示3:字段断点
Description:演不4:条件断点
Description:演示7:强制结束
复习:
//底层都会调用Integer的valueOf() //这个地方取得值是里面已经new好的Integer数组里面的对象,cache数组中每一个位置都是一个new Integer()对象, //所以他们的地址值都是相同的,都是取的cache数组中的第129位 //享元设计模式 Integer m = 1; //自动装箱 Integer n = 1; System.out.println(m == n); //true