1、static关键字修饰属性和方法
static关键字的使用
1. static: 静态的
2. static 用来修饰的结构:属性、方法; 代码块、内部类;
3. static修饰属性
3.1 复习:变量的分类
方式1:按照数据类型:基本数据类型、引用数据类型
方式2:按照类中声明的位置:
成员变量:按照是否使用static修饰进行分类:
使用static修饰的成员变量:静态变量、类变量
不使用static修饰的成员变量:非静态变量、实例变量
局部变量:方法内、方法形参、构造器内、构造器形参、代码块内等。
public class ChineseTest {
public static void main(String[] args) {
System.out.println(Chinese.nation);
Chinese.show();
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
c1.show();
ChineseTest.test();
}
public static void test(){
System.out.println("我是static的测试方法");
}
}
class Chinese{ //中国人类
//非静态变量、实例变量
String name;
int age;
//静态变量、类变量
static String nation = "中国";
@Override
public String toString() {
return "Chinese{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void eat(String food){
System.out.println("我喜欢吃" + food);
}
public static void show(){
System.out.println("我是一个中国人");
//调用静态的结构
System.out.println("nation = " + Chinese.nation);
method1();
//调用非静态的结构
// System.out.println("name = " + this.name);
// this.eat("饺子");
}
public static void method1(){
System.out.println("我是静态的测试方法");
}
public void method2(){
System.out.println("我是非静态的测试方法");
//调用非静态的结构
System.out.println("name = " + this.name);
this.eat("饺子");
//调用静态的结构
System.out.println("nation = " + nation);
method1();
}
public static String getNation() {
return nation;
}
public static void setNation(String nation) {
Chinese.nation = nation;
}
}
3.2 静态变量:类中的属性使用static进行修饰。
对比静态变量与实例变量:
① 个数
>静态变量:在内存空间中只有一份,被类的多个对象所共享。
>实例变量:类的每一个实例(或对象)都保存着一份实例变量。
② 内存位置
>静态变量:jdk6及之前:存放在方法区。 jdk7及之后:存放在堆空间
>实例变量:存放在堆空间的对象实体中。
③ 加载时机
>静态变量:随着类的加载而加载,由于类只会加载一次,所以静态变量也只有一份。
>实例变量:随着对象的创建而加载。每个对象拥有一份实例变量。
④ 调用者
>静态变量:可以被类直接调用,也可以使用对象调用。
>实例变量:只能使用对象进行调用。
⑤ 判断是否可以调用 ---> 从生命周期的角度解释
类变量 实例变量
类 yes no
对象 yes yes
⑥ 消亡时机
>静态变量:随着类的卸载而消亡
>实例变量:随着对象的消亡而消亡
4. static修饰方法:(类方法、静态方法)
> 随着类的加载而加载
> 可以通过“类.静态方法”的方式,直接调用静态方法
> 静态方法内可以调用静态的属性或静态的方法。(属性和方法的前缀使用的是当前类,可以省略)
不可以调用非静态的结构。(比如:属性、方法)
> 类方法 实例方法
类 yes no
对象 yes yes
> static修饰的方法内,不能使用this和super
> 补充:在类的非静态方法中,可以调用当前类中的静态结构(属性、方法)或非静态结构(属性、方法)
public class ChineseTest {
public static void main(String[] args) {
System.out.println(Chinese.nation);
Chinese.show();
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
c1.show();
ChineseTest.test();
}
public static void test(){
System.out.println("我是static的测试方法");
}
}
class Chinese{ //中国人类
//非静态变量、实例变量
String name;
int age;
//静态变量、类变量
static String nation = "中国";
@Override
public String toString() {
return "Chinese{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void eat(String food){
System.out.println("我喜欢吃" + food);
}
public static void show(){
System.out.println("我是一个中国人");
//调用静态的结构
System.out.println("nation = " + Chinese.nation);
method1();
//调用非静态的结构
// System.out.println("name = " + this.name);
// this.eat("饺子");
}
public static void method1(){
System.out.println("我是静态的测试方法");
}
public void method2(){
System.out.println("我是非静态的测试方法");
//调用非静态的结构
System.out.println("name = " + this.name);
this.eat("饺子");
//调用静态的结构
System.out.println("nation = " + nation);
method1();
}
public static String getNation() {
return nation;
}
public static void setNation(String nation) {
Chinese.nation = nation;
}
}
5. 开发中,什么时候需要将属性声明为静态的?
> 判断当前类的多个实例是否能共享此成员变量,且此成员变量的值是相同的。
> 开发中,常将一些常量声明是静态的。比如:Math类中的PI
什么时候需要将方法声明为静态的?
> 方法内操作的变量如果都是静态变量(而非实例变量)的话,则此方法建议声明为静态方法
> 开发中,常常将工具类中的方法,声明为静态方法。比如:Arrays类、Math类
2、static的应用举例及练习
public class CircleTest {
public static void main(String[] args) {
Circle c1 = new Circle();
System.out.println(c1);
Circle c2 = new Circle();
System.out.println(c2);
Circle c3 = new Circle();
System.out.println(c3);
Circle c4 = new Circle(2.3);
System.out.println(c4);
System.out.println(Circle.total);
}
}
class Circle{
double radius;//实例变量
int id;//编号
static int total;//创建的Circle实例的个数
public Circle(){
this.id = init;
init++;
total++;
}
public Circle(double radius){
this();
this.radius = radius;
}
private static int init = 1001; //自动给id赋值的基数
@Override
public String toString() {
return "Circle{" +
"radius=" + radius +
", id=" + id +
'}';
}
}
案例1:
编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,
定义封装这些属性的方法。账号要自动生成。
编写主类,使用银行账户类,输入、输出3个储户的上述信息。
考虑:哪些属性可以设计成static属性。
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 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());
}
}
案例2:
自定义一个数组的工具类,封装常用的数组算法
public class MyArrays {
/**
* 获取int[]数组的最大值
* @param arr 要获取最大值的数组
* @return 数组的最大值
*/
public static int getMax(int[] arr){
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if(max < arr[i]){
max = arr[i];
}
}
return max;
}
/**
* 获取int[]数组的最小值
* @param arr 要获取最小值的数组
* @return 数组的最小值
*/
public static int getMin(int[] arr){
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if(min > arr[i]){
min = arr[i];
}
}
return min;
}
public static int getSum(int[] arr){
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
public static int getAvg(int[] arr){
return getSum(arr) / arr.length;
}
public static void print(int[] arr){ //[12,231,34]
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
if(i == 0){
System.out.print(arr[i]);
}else{
System.out.print("," + arr[i]);
}
}
System.out.println("]");
}
public static int[] copy(int[] arr){
int[] newArr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
return newArr;
}
public static void reverse(int[] arr){
for(int i = 0,j = arr.length - 1;i < j;i++,j--){
//交互arr[i] 与 arr[j]位置的元素
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
public static void sort(int[] arr){
for(int j = 0;j < arr.length - 1;j++){
for (int i = 0; i < arr.length - 1 - j; i++) {
if(arr[i] > arr[i + 1]){
//交互arr[i] 和 arr[i + 1]
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
}
/**
* 使用线性查找的算法,查找指定的元素
* @param arr 待查找的数组
* @param target 要查找的元素
* @return target元素在arr数组中的索引位置。若未找到,则返回-1
*/
public static int linearSearch(int[] arr,int target){
for(int i = 0;i < arr.length;i++){
if(target == arr[i]){
return i;
}
}
//只要代码执行到此位置,一定是没找到
return -1;
}
}
public class MyArraysTest {
public static void main(String[] args) {
int[] arr = new int[]{34,56,223,2,56,24,56,67,778,45};
//求最大值
System.out.println("最大值为:" + MyArrays.getMax(arr));
//求平均值
System.out.println("平均值为:" + MyArrays.getAvg(arr));
//遍历
MyArrays.print(arr);
//查找
int index = MyArrays.linearSearch(arr,24);
if(index >= 0){
System.out.println("找到了,位置为:" + index);
}else{
System.out.println("未找到");
}
//排序
MyArrays.sort(arr);
//遍历
MyArrays.print(arr);
}
}
3、单例设计模式与mai()的理解
1. 设计模式概述:
设计模式是在大量的`实践中总结`和`理论化`之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。
就像是经典的棋谱,不同的棋局,我们用不同的棋谱。"套路"
经典的设计模式一共有23种。
2. 何为单例模式(Singleton):
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
3. 如何实现单例模式(掌握):
> 饿汉式
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);
}
}
//饿汉式
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) {
}
}
//懒汉式
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;
}
}
4. 对比两种模式(特点、优缺点)
特点:
> 饿汉式:“立即加载”,随着类的加载,当前的唯一实例就创建了
> 懒汉式:"延迟加载",在需要使用的时候,进行创建。
优缺点:
> 饿汉式:(优点)写法简单,由于内存中较早加载,使用更方便、更快。是线程安全的。 (缺点)内存中占用时间较长。
> 懒汉式:(缺点)线程不安全 (放到多线程章节时解决)(优点)在需要的时候进行创建,节省内存空间。
main()方法的剖析
public static void main(String args[]){}
1. 理解1:看做是一个普通的静态方法
理解2:看做是程序的入口,格式是固定的。
2. 与控制台交互
如何从键盘获取数据?
>方式1:使用Scanner
>方式2:使用main()的形参进行传值。
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]);
}
}
}
4、代码块
类的成员之四:代码块
回顾:类中可以声明的结构:属性、方法、构造器;代码块(或初始化块)、内部类
1. 代码块(或初始化块)的作用:
用来初始化类或对象的信息(即初始化类或对象的成员变量)
2. 代码块的修饰:
只能使用static进行修饰。
3. 代码块的分类:
静态代码块:使用static修饰
非静态代码块:没有使用static修饰
4. 具体使用:
4.1 静态代码块:
> 随着类的加载而执行
> 由于类的加载只会执行一次,进而静态代码块的执行,也只会执行一次
> 作用:用来初始化类的信息
> 内部可以声明变量、调用属性或方法、编写输出语句等操作。
> 静态代码块的执行要先于非静态代码块的执行
> 如果声明有多个静态代码块,则按照声明的先后顺序执行
> 静态代码块内部只能调用静态的结构(即静态的属性、方法),不能调用非静态的结构(即非静态的属性、方法)
4.2 非静态代码块:
> 随着对象的创建而执行
> 每创建当前类的一个实例,就会执行一次非静态代码块
> 作用:用来初始化对象的信息
> 内部可以声明变量、调用属性或方法、编写输出语句等操作。
> 如果声明有多个非静态代码块,则按照声明的先后顺序执行
> 非静态代码块内部可以调用静态的结构(即静态的属性、方法),也可以调用非静态的结构(即非静态的属性、方法)
public class BlockTest {
public static void main(String[] args) {
System.out.println(Person.info);
System.out.println(Person.info);
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1.age);//1
// p1.eat();
}
}
class Person{
String name;
int age;
static String info = "我是一个人";
public void eat(){
System.out.println("人吃饭");
}
public Person(){}
//非静态代码块
{
System.out.println("非静态代码块2");
}
{
System.out.println("非静态代码块1");
age = 1;
System.out.println("info = " + info);
}
//静态代码块
static{
System.out.println("静态代码块2");
}
static{
System.out.println("静态代码块1");
System.out.println("info = " + info);
// System.out.println("age = " + age);
// eat();
}
}
案例1:
(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方法的代码
public class User {
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;
}
public User(){
System.out.println("新用户注册");
registrationTime = System.currentTimeMillis();//获取系统当前时间 (距离1970-1-1 00:00:00的毫秒数)
userName = System.currentTimeMillis() + "";
password = "123456";
}
public User(String userName,String password){
System.out.println("新用户注册");
registrationTime = System.currentTimeMillis();
this.userName = userName;
this.password = password;
}
public String getInfo(){
return "用户名:" + userName + ", 密码:" + password + ",注册时间:" + registrationTime;
}
}
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(){
userName = System.currentTimeMillis() + "";
password = "123456";
}
public User1(String userName,String password){
// System.out.println("新用户注册");
// registrationTime = System.currentTimeMillis();
this.userName = userName;
this.password = password;
}
public String getInfo(){
return "用户名:" + userName + ", 密码:" + password + ",注册时间:" + registrationTime;
}
}
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());
}
}
5、类中属性赋值的位置及过程
1. 可以给类的非静态的属性(即实例变量)赋值的位置有:
① 默认初始化
② 显式初始化 或 ⑤ 代码块中初始化
③ 构造器中初始化
***********************
④ 有了对象以后,通过"对象.属性"或"对象.方法"的方法进行赋值
2. 执行的先后顺序:
① - ②/⑤ - ③ - ④
3. (超纲)关于字节码文件中的<init>的简单说明:(通过插件jclasslib bytecode viewer查看)
> <init>方法在字节码文件中可以看到。每个<init>方法都对应着一个类的构造器。(类中声明了几个构造器就会有几个<init>)
> 编写的代码中的构造器在编译以后就会以<init>方法的方式呈现
> <init>方法内部的代码包含了实例变量的显示赋值、代码块中的赋值和构造器中的代码。
> <init>方法用来初始化当前创建的对象的信息的。
4. 给实例变量赋值的位置很多,开发中如何选?
> 显示赋值:比较适合于每个对象的属性值相同的场景
> 构造器中赋值:比较适合于每个对象的属性值不相同的场景
由父及子 静态先行
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;
}
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;
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(){
//
// }
}
6、final修饰的使用
final关键字的使用
1. final的理解:最终的
2. final可以用来修饰的结构:类、方法、变量
3. 具体说明:
3.1 final修饰类:表示此类不能被继承。
比如:String、StringBuffer、StringBuilder类
3.2 final修饰方法:表示此方法不能被重写
比如:Object类中的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;
}
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;
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(){
//
// }
}
7、抽象类与抽象方法的使用
抽象类与抽象方法
1. 案例引入
举例1:GeometricObject-Circle-Rectangle
abstract class GeometricObject{ //几何图形
//求面积 (只能考虑提供方法的声明,而没有办法提供方法体。所以,此方法适合声明为抽象方法)
//求周长(只能考虑提供方法的声明,而没有办法提供方法体。所以,此方法适合声明为抽象方法)
}
class Circle extends GeometricObject{
//求面积 (必须重写(或实现)父类中的抽象方法)
//求周长(必须重写(或实现)父类中的抽象方法)
}
class Rectangle extends GeometricObject{
//求面积 (必须重写(或实现)父类中的抽象方法)
//求周长(必须重写(或实现)父类中的抽象方法)
}
举例2:Account-SavingAccount-CheckAcount
abstract 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修饰的类不能有子类
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(); //抽象方法
}
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("学生应该多呼吸新鲜空气");
}
}
public abstract class Worker extends Person{
@Override
public void eat() {
System.out.println("工人很辛苦,多吃");
}
}
public abstract class Creature { //生物类
public abstract void breath();//呼吸
}
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();
}
}
8、模板方法设计模式
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 {
@Override
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);
}
}
}
}
案例:
编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个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=xxxx年xx月xx日]
(4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:
private成员变量wage和hour;
提供必要的构造器;
实现父类的抽象方法earnings(),该方法返回wage*hour值;
toString()方法输出员工类型信息及员工的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);
public abstract class Employee {
private String name;
private int number;
private MyDate birthday;
public Employee() {
}
public Employee(String name, int number, MyDate birthday) {
this.name = name;
this.number = number;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
public abstract double earnings();
public String toString(){
return "name = " + name + ",number = " + number +
", birthday = " + birthday.toDateString();
}
}
public class HourlyEmployee extends Employee{
private double wage;//单位小时的工资
private int hour;//月工作的小时数
public HourlyEmployee() {
}
public HourlyEmployee(String name, int number, MyDate birthday, double wage, int hour) {
super(name, number, birthday);
this.wage = wage;
this.hour = hour;
}
public double getWage() {
return wage;
}
public void setWage(double wage) {
this.wage = wage;
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
@Override
public double earnings() {
return wage * hour;
}
public String toString(){
return "HourlyEmployee[" + super.toString() + "]";
}
}
public class MyDate {
private int year;
private int month;
private int day;
public MyDate() {
}
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public String toDateString(){
return year + "年" + month + "月" + day + "日";
}
}
public class SalariedEmployee extends Employee{
private double monthlySalary;//月工资
public SalariedEmployee() {
}
@Override
public double earnings() {
return monthlySalary;
}
public SalariedEmployee(String name, int number, MyDate birthday, double monthlySalary) {
super(name, number, birthday);
this.monthlySalary = monthlySalary;
}
// public double getMonthlySalary() {
// return monthlySalary;
// }
public void setMonthlySalary(double monthlySalary) {
this.monthlySalary = monthlySalary;
}
public String toString(){
return "SalariedEmployee[" + super.toString() + "]";
}
}
public class PayrollSystem {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
Employee[] emps = new Employee[2];
emps[0] = new SalariedEmployee("张小亮",1001,
new MyDate(1992,12,21),18000);
emps[1] = new HourlyEmployee("侯少鹏",1002,new MyDate(1997,11,12),
240,100);
System.out.println("请输入当前的月份:");
int month = scan.nextInt();
for (int i = 0; i < emps.length; i++) {
System.out.println(emps[i].toString());
System.out.println("工资为:" + emps[i].earnings());
if(month == emps[i].getBirthday().getMonth()){
System.out.println("生日快乐!加薪100");
}
}
scan.close();
}
}
9、接口的使用
接口的使用
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 实现类对象;
9. 面试题:区分抽象类和接口
> 共性:都可以声明抽象方法
都不能实例化
> 不同:① 抽象类一定有构造器。接口没有构造器
② 类与类之间继承关系,类与接口之间是实现关系,接口与接口之间是多继承关系
public class USBTest {
public static void main(String[] args) {
//1.创建接口实现类的对象
Computer computer = new Computer();
Printer printer = new Printer();
computer.transferData(printer);
//2.创建接口实现类的匿名对象
computer.transferData(new Camera());
System.out.println();
//3.创建接口匿名实现类的对象
USB usb1 = new USB(){
public void start(){
System.out.println("U盘开始工作");
}
public void stop(){
System.out.println("U盘结束工作");
}
};
computer.transferData(usb1);
//4. 创建接口匿名实现类的匿名对象
computer.transferData(new USB(){
public void start(){
System.out.println("扫描仪开始工作");
}
public void stop(){
System.out.println("扫描仪结束工作");
}
});
}
}
class Computer{
public void transferData(USB usb){ //多态:USB usb = new Printer();
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();
}
10、接口的课后练习
案例1:
1、声明接口Eatable,包含抽象方法public abstract void eat();
2、声明实现类中国人Chinese,重写抽象方法,打印用筷子吃饭
3、声明实现类美国人American,重写抽象方法,打印用刀叉吃饭
4、声明实现类印度人Indian,重写抽象方法,打印用手抓饭
5、声明测试类EatableTest,创建Eatable数组,存储各国人对象,并遍历数组,调用eat()方法
public interface Eatable {
void eat();
}
public class Chinese implements Eatable{
@Override
public void eat() {
System.out.println("中国人使用筷子吃饭");
}
}
public class American implements Eatable{
@Override
public void eat() {
System.out.println("美国人使用刀叉吃饭");
}
}
public class Indian implements Eatable{
@Override
public void eat() {
System.out.println("印度人使用手抓饭");
}
}
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();
}
}
}
案例2:
定义一个接口用来实现两个对象的比较。
interface CompareObject{
//若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
public int compareTo(Object o);
}
定义一个Circle类,声明radius属性,提供getter和setter方法
定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。
在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小。
拓展:参照上述做法定义矩形类Rectangle和ComparableRectangle类,在ComparableRectangle类
中给出compareTo方法的实现,比较两个矩形的面积大小。
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 +
'}';
}
}
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("输入的类型不匹配");
}
}
}
public interface CompareObject {
//若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
public int compareTo(Object o);
}
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一样大");
}
}
}
案例3:
阿里的一个工程师Developer,结构见图。
其中,有一个乘坐交通工具的方法takingVehicle(),在此方法中调用交通工具的run()。
为了出行方便,他买了一辆捷安特自行车、一辆雅迪电动车和一辆奔驰轿车。这里涉及到的相关类及接口关系如图。
其中,电动车增加动力的方式是充电,轿车增加动力的方式是加油。在具体交通工具的run()中调用其所在类
的相关属性信息。
请编写相关代码,并测试。
提示:创建Vehicle[]数组,保存阿里工程师的三辆交通工具,并分别在工程师的takingVehicle()中调用。
public class Bicycle extends Vehicle{
public Bicycle() {
}
public Bicycle(String brand, String color) {
super(brand, color);
}
@Override
public void run() {
System.out.println("自行车通过人力脚蹬行驶");
}
}
public class Car extends Vehicle implements IPower{
private String carNumber;
public Car() {
}
public Car(String brand, String color, String carNumber) {
super(brand, color);
this.carNumber = carNumber;
}
public String getCarNumber() {
return carNumber;
}
public void setCarNumber(String carNumber) {
this.carNumber = carNumber;
}
@Override
public void run() {
System.out.println("汽车通过内燃机驱动行驶");
}
@Override
public void power() {
System.out.println("汽车通过汽油提供动力");
}
}
public class Developer {
private String name;
private int age;
public Developer() {
}
public Developer(String name, int age) {
this.name = name;
this.age = age;
}
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 void takingVehicle(Vehicle vehicle){
vehicle.run();
}
}
public class ElectricVehicle extends Vehicle implements IPower{
public ElectricVehicle() {
}
public ElectricVehicle(String brand, String color) {
super(brand, color);
}
@Override
public void run() {
System.out.println("电动车通过电机驱动行驶");
}
@Override
public void power() {
System.out.println("电动车使用电力提供动力");
}
}
public interface IPower {
void power();
}
public abstract class Vehicle {
private String brand;//品牌
private String color;//颜色
public Vehicle() {
}
public Vehicle(String brand, String color) {
this.brand = brand;
this.color = color;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public abstract void run();
}
public class VehicleTest {
public static void main(String[] args) {
Developer developer = new Developer();
//创建三个交通工具,保存在数组中
Vehicle[] vehicles = new Vehicle[3];
vehicles[0] = new Bicycle("捷安特","骚红色");
vehicles[1] = new ElectricVehicle("雅迪","天蓝色");
vehicles[2] = new Car("奔驰","黑色","沪Au888");
for (int i = 0;i < vehicles.length;i++){
developer.takingVehicle(vehicles[i]);
if(vehicles[i] instanceof IPower){
((IPower) vehicles[i]).power();
}
}
}
}
10、内部类
类的成员之五:内部类
1. 什么是内部类?
将一个类A定义在另一个类B里面,里面的那个类A就称为`内部类(InnerClass)`,类B则称为`外部类(OuterClass)`。
2. 为什么需要内部类?
具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A
提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类。
总的来说,遵循`高内聚、低耦合`的面向对象开发原则。
3. 内部类使用举例:
Thread类内部声明了State类,表示线程的生命周期
HashMap类中声明了Node类,表示封装的key和value
4. 内部类的分类:(参考变量的分类)
> 成员内部类:直接声明在外部类的里面。
> 使用static修饰的:静态的成员内部类
> 不使用static修饰的:非静态的成员内部类
> 局部内部类:声明在方法内、构造器内、代码块内的内部类
> 匿名的局部内部类
> 非匿名的局部内部类
5. 内部类这节要讲的知识:
> 成员内部类的理解
> 如何创建成员内部类的实例
> 如何在成员内部类中调用外部类的结构
> 局部内部类的基本使用
6. 关于成员内部类的理解:
> 从类的角度看:
- 内部可以声明属性、方法、构造器、代码块、内部类等结构
- 此内部类可以声明父类,可以实现接口
- 可以使用final修饰
- 可以使用abstract修饰
> 从外部类的成员的角度看:
- 在内部可以调用外部类的结构。比如:属性、方法等
- 除了使用public、缺省权限修饰之外,还可以使用private、protected修饰
- 可以使用static修饰
案例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("尚硅谷");
}
}
11、枚举的两种定义方式
枚举类的使用
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 之前如何自定义枚举类 (了解)
见代码
public class SeasonTest {
public static void main(String[] args) {
// Season.SPRING = 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 + '\'' +
'}';
}
}
5. JDK5.0中使用enum定义枚举类
见代码
public class SeasonTest1 {
public static void main(String[] args) {
System.out.println(Season1.SPRING.getClass());
System.out.println(Season1.SPRING.getClass().getSuperclass());
System.out.println(Season1.SPRING.getClass().getSuperclass().getSuperclass());
}
}
enum Season1 {
SPRING("春天", "春暖花开"),
SUMMER("夏天", "下日燕燕"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "白雪皑皑");
private final String seasonName;
private final String seasonDesc;
private Season1(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
6. Enum中的常用方法:
6.1 使用enum关键字定义的枚举类,默认其父类是java.lang.Enum类
使用enum关键字定义的枚举类,不要再显示的定义其父类。否则报错。
6.2 熟悉Enum类中常用的方法
String toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法!
(关注)static 枚举类型[] values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法
(关注)static 枚举类型 valueOf(String name):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
String name():得到当前枚举常量的名称。建议优先使用toString()。
int ordinal():返回当前枚举常量的次序号,默认从0开始
7. 枚举类实现接口的操作
情况1:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是同一个方法。
情况2:让枚举类的每一个对象重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是不同的实现的方法。
public class SeasonTest1 {
public static void main(String[] args) {
// System.out.println(Season1.SPRING.getClass());
// System.out.println(Season1.SPRING.getClass().getSuperclass());
// System.out.println(Season1.SPRING.getClass().getSuperclass().getSuperclass());
//测试方法
//1. toString()
System.out.println(Season1.SPRING);
System.out.println(Season1.AUTUMN);
//2. name()
System.out.println(Season1.SPRING.name());
//3. vlaues()
Season1[] values = Season1.values();
for (int i = 0; i < values.length; i++) {
System.out.println(values[i]);
}
//4. valueOf(String objName):返回当前枚举类中名称为objName的枚举类对象。
//如果枚举类中不存在objName名称的对象,则报错。
String objName = "WINTER";
// objName = "WINTER1";
Season1 season1 = Season1.valueOf(objName);
System.out.println(season1);
//5.ordinal()
System.out.println(Season1.AUTUMN.ordinal());
//通过枚举类的对象调用重写接口中的方法
Season1.SUMMER.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;
}
//4. 提供实例变量的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("这是一个季节");
}
}
案例1:
案例:使用枚举类实现单例模式
public class BankTest1 {
public static void main(String[] args) {
// Bank1.instance = null;
System.out.println(GirlFriend.XIAO_LI);
}
}
//jdk5.0之前的使用枚举类定义单例模式
class Bank1{
private Bank1(){}
public static final Bank1 instance = new Bank1();
}
//jdk5.0使用enum关键字定义枚举类的方式定义单例模式
enum Bank2{
CPB;
}
enum GirlFriend{
XIAO_LI(20);
private final int age;
private GirlFriend(int age){
this.age = age;
}
}
案例2:
案例:颜色枚举类Color(使用enum声明)
1、声明颜色枚举类:7个常量对象:RED, ORANGE, YELLOW, GREEN, CYAN, BLUE,PURPLE;
2、在测试类中,使用枚举类,获取绿色对象,并打印对象。
public enum Color {
RED, ORANGE, YELLOW, GREEN, CYAN, BLUE,PURPLE;
}
案例3:
案例拓展:颜色枚举类(使用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,PURPLE
public class ColorTest {
public static void main(String[] args) {
System.out.println(Color.GREEN);
}
}
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;
}
}
12、Annotation的注解、单元测试
注解的使用
1. Annotation的理解
> 注解(Annotation)是从`JDK5.0`开始引入,以“`@注解名`”在代码中存在。
> Annotation 可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。
还可以添加一些参数值,这些信息被保存在 Annotation 的 “name=value” 对中。
> 注解可以在类编译、运行时进行加载,体现不同的功能。
2. 注解的应用场景:
示例1:生成文档相关的注解
示例2:在编译时进行格式检查(JDK内置的三个基本注解)
示例3:跟踪代码依赖性,实现替代配置文件功能
3. Java基础涉及到的三个常用注解
`@Override`: 限定重写父类方法,该注解只能用于方法
`@Deprecated`: 用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
`@SuppressWarnings`: 抑制编译器警告
public class AnnotationTest {
public static void main(String[] args) {
Person p1 = new Student();
p1.walk();
Date date = new Date();
System.out.println(date);
Date date1 = new Date(2022,11,29);
System.out.println(date1);
Person p2 = new Person();
@SuppressWarnings({"RedundantExplicitVariableType"}) Person p3 = new Person("Tom");
System.out.println(p3);
@SuppressWarnings("unused") int num = 10;
}
}
@MyAnnotation(value="class")
class Person{
String name;
int age;
@MyAnnotation()
public Person(){}
@Deprecated
public Person(String name){
this.name = name;
}
public void eat(){
System.out.println("人吃饭");
}
public void walk(){
System.out.println("人走路");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Student extends Person{
@Override
public void eat(){
System.out.println("学生吃饭");
}
@Override
public void walk(){
System.out.println("学生走路");
}
}
4. 自定义注解
以@SuppressWarnings为参照,进行定义即可。
5. 元注解的理解:
元注解:对现有的注解进行解释说明的注解。
讲4个元注解:
(1)@Target:用于描述注解的使用范围
可以通过枚举类型ElementType的10个常量对象来指定
TYPE,METHOD,CONSTRUCTOR,PACKAGE.....
(2)@Retention:用于描述注解的生命周期
可以通过枚举类型RetentionPolicy的3个常量对象来指定
SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时)
唯有RUNTIME阶段才能被反射读取到。
(3)@Documented:表明这个注解应该被 javadoc工具记录。
(4)@Inherited:允许子类继承父类中的注解
拓展: 元数据。
String name = "Tom";
框架 = 注解 + 反射 + 设计模式
12、包装类的理解
包装类的使用
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(xxx xx)
(拆箱)包装类 ---> 基本数据类型:调用包装类的xxxValue()
注意:原来使用基本数据类型变量的位置,改成包装类以后,对于成员变量来说,其默认值变化了!
jdk5.0新特性:自动装箱、自动拆箱。
public class WrapperTest {
/*
基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
包装类 ---> 基本数据类型:调用包装类的xxxValue()
*/
//包装类 ---> 基本数据类型:调用包装类的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();
}
//基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
@Test
public void test1(){
int i1 = 10;
Integer ii1 = new Integer(i1);
System.out.println(ii1.toString());
float f1 = 12.3F;
f1 = 32.2f;
Float ff1 = new Float(f1);
System.out.println(ff1.toString());
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);
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;
double balance1; //0.0
Double balance2; //null 0.0
}
4. String 与 基本数据类型、包装类之间的转换。
基本数据类型、包装类 ---> String类型:① 调用String的重载的静态方法valueOf(xxx xx) ; ② 基本数据类型的变量 + ""
String类型 ---> 基本数据类型、包装类: 调用包装类的静态方法:parseXxx()
public class WrapperTest1 {
/*
基本数据类型、包装类 ---> String类型:① 调用String的重载的静态方法valueOf(xxx xx) ; ② 基本数据类型的变量 + ""
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的重载的静态方法valueOf(xxx xx)
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 + "";
}
}
案例1:
利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级。
提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动态伸缩。
1、创建Vector对象:Vector v=new Vector();
2、给向量添加元素:v.addElement(Object obj); //obj必须是对象
3、取出向量中的元素:Object obj = v.elementAt(0);
注意第一个元素的下标是0,返回值是Object类型的。
4、计算向量的长度:v.size();
5、若与最高分相差10分内:A等;20分内:B等;30分内:C等;其它:D等
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){ //for(;;)
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 is " + score + " grade is " + grade);
}
scanner.close();
}
}