关键字:static
static关键字的使用
static:静态的。
可以用来修饰:属性、方法、代码块、内部类。
使用static来修饰属性:静态变量
属性:按是否使用是static修饰,又分为静态属性和非静态属性(实例变量)
- 实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样属性值的修改。
- 静态变量:我们创建了类的多个对象,多个对象共享一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用次静态变量时,是修改过的。
static修饰属性的其他说明:
静态变量随着类的加载而加载,可以通过“类.静态变量”的方式调用;
静态变量的加载要早于对象的创建;
由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
类变量 实例变量 类 yes no 对象 yes yes
静态属性举例:System.out、Math.PI。
使用static来修饰方法:静态方法
① 随着类的加载而加载,可以通过“类.静态方法”的方式进行调用;
② 静态方法 非静态方法 类 yes no 对象 yes yes ③ 静态方法中,只能调用静态的方法或属性;非静态方法中,既可以调用静态的方法或属性,也可以调用非静态的方法或属性。
static注意点:
① 在静态方法内不能使用this关键字和super关键字;
② 关于静态属性和静态方法的使用,都从生命周期的角度去解释。
开发中,如何确定一个属性是否要声明为static的?
- 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
- 类中的常量也常常声明为static。
开发中,如何确定一个方法是否要声明为static的?
- 操作静态属性的方法,通常设置为static的;
- 工具类中的方法,习惯上声明为static的。比如:Math、Arrays、Collections。
代码示例
package top.triabin._01static;
/*
* static关键字的使用:代码示例
*/
public class StaticTest {
public static void main(String[] args) {
Chinese.nation = "中国";
Chinese c1 = new Chinese();
System.out.println("通过“类.静态变量”的方式赋值的静态变量:" + c1.nation);
c1.name = "姚明";
c1.age = 40;
c1.nation = "CHN";
Chinese c2 = new Chinese();
c2.name = "马龙";
c2.age = 30;
System.out.println(c1.nation + " \t" +c2.nation );//CHN CHN
c2.nation = "CHINA";
System.out.println(c1.nation + "\t" +c2.nation);//CHINA CHINA
// Chinese.name = "张继科";//编译不通过,不能使用类去调用非静态属性
c1.eat();
Chinese.show();
// Chinese.eat();//编译不通过,不能使用类去调用非静态方法
}
}
class Chinese{
String name;
int age;
static String nation;
public void eat() {
System.out.println("中国人吃中餐");
//调用非静态结构
this.info();
System.out.println("name: " + name);
//调用静态结构
walk();
System.out.println("nation: " + Chinese.nation);
}
public static void show() {
System.out.println("我是一个中国人!");
// eat();//静态方法中,只能调用静态的方法或属性
// name = "Triabin"
System.out.println(Chinese.nation);//可以调用静态的属性或方法,属性前可以省略了“类名.”(Chinese.)
}
public void info() {
System.out.println("name: " + name + ",age: " + age);
}
public static void walk() {}
}- 运行结果
应用举例
package top.triabin._01static;
/*
* static关键字的使用:应用举例
*/
public class CircleTest {
public static void main(String[] args) {
Circle c1 = new Circle();
Circle c2 = new Circle();
Circle c3 = new Circle(3.4);
System.out.println("c1的id:" + c1.getId());
System.out.println("c2的id:" + c2.getId());
System.out.println("c3的id:" + c3.getId());
System.out.println("创建圆的个数为:" + Circle.getTotal());
}
}
class Circle{
private double radius;
private int id;
private static int total;//记录创建圆的个数
private static int init = 1001;//static声明的属性被所有对象所共享
public Circle() {
id = init++;
total++;
}
public Circle(double radius) {
this();
this.radius = radius;
}
public double findArea() {
return Math.PI * radius * radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public int getId() {
return id;
}
public static int getTotal() {
return total;
}
}- 运行结果
单例(Singleton)设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模免去我们自己再思考和摸索。就像是经典 的棋谱,不同的棋局,我们用不同的棋谱。”套路”
设计模式:
- 创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、创造者模式、原型模式;
- 结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式;
- 行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
PS:设计模式是独立于语言之外的,就像数据结构与算法。
单例设计模式只是上述设计模式中的一种。所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类**只能存在一个对象实例**,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
如何实现?
① 饿汉式实现
package top.triabin._01static;
/*
*单例模式的懒汉式实现
*/
public class SingletonTest1 {
public static void main(String[] args) {
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. 提供公共的静态的方法,返回类的对象
public static Bank getInstance() {
return instance;
}
}- 坏处:直接加载对象,对象加载时间过长;
- 好处:饿汉式是线程安全的。
② 懒汉式实现
package top.triabin._01static;
/*
* 单例模式的懒汉式实现
*/
public class SingletonTest2 {
public static void main(String[] args) {
Order order1 = Order.getInstance();
Order order2 = Order.getInstance();
System.out.println(order1 == order2);//true
}
}
class Order{
//1. 私有化类的构造器
private Order() {}
//2. 声明当前类对象没有初始化
//4. 此对象也必须声明为static的
private static Order instance = null;
//3. 声明public、static的返回当前类对象的方法
public static Order getInstance() {
if(instance == null) {
instance = new Order();
}
return instance;
}
}- 好处:到需要时才加载对象,延迟对象的创建;
- 目前写法坏处:线程不安全。—> 到多线程内容时再修改
单例模式的优点:由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
举例:java.lang.Runtime。
单例设计模式应用场景
- 网站的计数器,一般也是单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
- Application也是单例的典型应用。
- Windows的Task Manager(任务管理器)就是很典型的单例模式。
- Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
练习题
编写一个类实现银行账户的概念,包含的属性有“账号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。账号要自动生成。
编写主类,使用银行账户类,输入、输出3个储户的上述信息。
考虑:哪些属性可以设计成static属性。
- Account类
package top.triabin._01exercise1;
/*
* 课后练习题1:Account类
* 编写一个类实现银行账户的概念,包含的属性有“账号”、“密码”、“存款余额”、“利率”、
* “最小余额”,定义封装这些属性的方法。账号要自动生成。
*/
public class Account {
private int id;
private String pwd = "000000";
private double balance;
private static double interestRate;
private static double minBalance = 1.0;
private static int iterId = 1001;
private static int total;
public Account() {
id = iterId++;
total++;
}
public Account(String pwd,double balance) {
this();
this.pwd = pwd;
this.balance = balance;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
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;
}
public int getId() {
return id;
}
public static int getTotal() {
return total;
}
public String toString() {
return "Account [id=" + id + ", pwd=" + pwd + ", balance=" + balance + "]";
}
}- 主类Bank
package top.triabin._01exercise1;
/*
* 课后练习题1主类:Bank、
* 编写主类,使用银行账户类,输入、输出3个储户的上述信息。
*/
public class Bank {
public static void main(String[] args) {
Account a1 = new Account();
a1.setBalance(201314);
Account a2 = new Account("quqian",2000);
Account a3 = new Account("123455",1314);
Account.setInterestRate(3.5);
Account.setMinBalance(100);
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
System.out.println("最小余额 = " + Account.getMinBalance());
System.out.println("账户数量 = " + Account.getTotal());
System.out.println("利率 = " + Account.getInterestRate());
}
}- 运行结果
理解main方法的语法
main()方法的使用说明
main()作为程序的入口。
main()方法也是一个普通的静态方法。
package top.triabin._02main;
public class MainTest {
public static void main(String[] args) {
Main.main(new String[100]);
}
}
class Main{
public static void main(String[] args) {
args = new String[100];
for(int i = 0;i < args.length;i++) {
args[i] = "args_" + i;
System.out.println(args[i]);
}
}
}- 运行时会询问将哪一个main()方法作为程序入口
main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
package top.triabin._02main;
/*
* main()方法与控制台交互示例代码
*/
public class MainInteract {
public static void main(String[] args) {
for(int i = 0;i < args.length;i++) {
System.out.println("String型:" + args[i]);
int num = Integer.parseInt(args[i]);
System.out.println("int型:" + num);
}
}
}① Eclipse中
先运行使其编译一遍,然后:右键 -> Run As ->Run Configurations -> 左侧找到并选中相应类名 -> (x)= Arguments -> 在Program arguments处输入内容,不管加不加””号都以字符串形式输入 -> Run
如图:
运行
② 命令行中
- 先
javac MainInteract.java
->java MainInteract 77 99 88 66 55
-> 运行:
类的成员之四:代码块
基本概念和作用
代码块的作用:用来初始化类、对象。
代码块如果有修饰,只能是static。
分类:静态代码块、非静态代码块。
静态代码块:
- 内部可以有输出语句
- 随着类的加载而执行,而且只执行一次
- 作用:初始化类信息
- 如果一个类中定义了多个代码块,则按照声明的先后顺序执行
- 静态代码块的执行优于非静态代码块的执行
- 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
非静态代码块:
- 内部可以有输出语句
- 随着对象的创建而执行
- 每创建一个对象,就执行一次非静态代码块
- 作用:可以在创建对象时,对对象的属性进行初始化
- 非静态代码块内可以调用静态的属性、静态的方法或非静态的属性、非静态的方法
代码示例
package top.triabin._03codeblock;
/*
* 类的成员之四:代码块
*
* 1. 代码块的作用:用来初始化类、对象。
* 2. 代码块如果有修饰,只能是static。
* 3. 分类:静态代码块、非静态代码块
*
* 4. 静态代码块
*
* 5. 非静态代码块
*
*/
public class BlockTest {
public static void main(String[] args) {
String descr = Person.descr;
System.out.println(descr);
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1.age);
Person.info();
}
}
class Person{
//属性
String name;
int age;
static String descr = "我是一个人";
//构造器
public Person() {}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
//代码块
//static代码块
static {
System.out.println("hello,static block_1!");
//调用静态结构
descr = "我是一个爱学习的人1";
info();
// eat();
// name = "Triabin";//不能调用非静态结构
}
static {
System.out.println("hello,static block_2!");
}
//非static代码块
{
System.out.println("hello,block!");
//调用非静态结构
age = 1;
eat();
//调用静态结构
descr = "我是一个爱学习的人";
info();
}
//方法
public void eat() {
System.out.println("吃饭");
}
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public static void info() {
System.out.println("我是一个快乐的人");
}
}- 运行结果
应用举例
先父后子,静态先行
package top.triabin._03codeblock;
//总结:先父后子,静态先行
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();
System.out.println();
new Leaf();
}
}- 运行
package top.triabin._03codeblock;
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();
}
}- 运行
对属性可以赋值的位置总结:
① 默认初始化
② 显示初始化
③ 构造器中初始化
④ 有了对象以后,可以通过“对象.属性”或“对象.方法”的方式,进行赋值
⑤ 在代码块中赋值
执行的先后顺序:① - ② / ⑤ - ③ - ④
②和⑤谁写在前面就先执行谁。
关键字:final
基本概念和使用
final:最终的
- final可以用来修饰的结构:类、方法、变量
- final用来修饰一个类:此类不能被其他类继承。
- 比如:String类、System类、StringBuffer类
- final用来修饰方法:表明此方法不可以被重写。
- 比如:Object类中的getClass()
- final用来修饰变量:此时的”变量“就称为是一个常量
- final修饰属性:可以考虑赋值的位置有:显示初始化、代码块中初始化、构造器中初始化
- final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后就只能在方法体内使用此形参,但不能重新赋值。
static final用来修饰属性:全局常量
static final用来修饰方法:不可重写的静态方法
练习题
面试题:排错
public class Something{
public int addOne(final int x){
return ++x;//错误
//return x + 1;
}
}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;
}
抽象类与抽象方法
- 概念:随着继承层次的中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫抽象类。
abstract关键字的使用
abstract:抽象的。
abstract可以用来修饰:类、方法。
abstract修饰类:抽象类
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作
abstract修饰方法:抽象方法
抽象方法只有方法的声明,没有方法体
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法。
若子类重写了父类中所有抽象方法后,此子类可实例化
若子类没有重写父类中所有抽象方法,则次子类也是一个抽象类,需要使用abstract修饰
abstract使用上的注意点:
- abstract不能用来修饰:属性、构造器等结构
- abstract不能用来修饰私有方法、静态方法、final的方法
代码示例
package top.triabin._05abstract;
/*
* abstract关键字的使用:
* 1. abstract:抽象的
* 2. abstract可以用来修饰:类、方法
* 3. abstract修饰类:抽象类
* 4. abstract修饰方法:抽象方法
* 5. abstract使用上的注意点:
*/
public class AbstractTest {
public static void main(String[] args) {
//一旦Person类抽象了,就不可实例化
// Person p1 = new Person();//Cannot instantiate the type Person
// p1.eat();
}
}
abstract class Person{
String name;
int age;
public Person() {}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
//不是抽象方法
// public void eat() {
//
// }
//抽象方法
public abstract void eat();
public void walk() {
System.out.println("人走路");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
}
public void eat() {
// TODO Auto-generated method stub
//必须重写父类中所有的抽象方法,否则子类无法实例化
}
}
抽象类的匿名子类
/* |
多态的应用:模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
- 当功能内部的一部分实现是确定的,一部分实现是不确定的。这时可以吧不确定的部分暴露出去,让子类去实现。
- 换句话说,在软件开发在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
举例
package top.triabin._05abstract;
/*
* 抽象类的应用:模板方法的设计模式
*
*
*/
public abstract class TemplateTest {
public static void main(String[] args) {
Template t = new SubTemlate();
t.spendTime();
}
}
abstract class Template{
/**
*
* @Description 计算某段代码执行所需要花费的时间
* @author Dawn Lee
* @version 1.0
* @data 2020-8-2416:07:07
*
*/
public void spendTime() {
long start = System.currentTimeMillis();
code();//不确定的部分,易变的部分
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (start - end));
}
public abstract void code();
}
class SubTemlate extends Template{
private int count = 0;
public void code() {
for(int i = 2;i <= 1000;i++) {
boolean isFlag = true;
for(int j = 2;j <= Math.sqrt(i);j++) {
if(i % j == 0) {
isFlag = false;
break;
}
}
if(isFlag) {
System.out.print(i + " ");
count += 1;
if(count == 10) {
System.out.println();
count = 0;
}
}
}
}
}- 运行
模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:
- 数据库访问的封装
- Junit单元测试
- JavaWeb的Servlet中关于doGet/doPost方法调用
- Hibernate中模板程序
- Spring中JDBCTemlate、HibernateTemplate等
练习题
编写一个Employee类,声明为抽象类,包含如下三个属性:
name,id,salary。
提供必要的构造器和抽象方法:work()。
对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。
package top.triabin._05exercise1;
/**
*
* @Description 编写一个Employee类,声明为抽象类,包含如下三个属性:
* name,id,salary。
* 提供必要的构造器和抽象方法:work()。
* @author DawnLee
* @version 1.0
* @data 2020-8-24 9:59:51
*
*/
abstract public class Employee {
private String name;//姓名
private int id;//工号
private double salary;//薪资
public Employee() {
super();
}
public Employee(String name, int id, double salary) {
super();
this.name = name;
this.id = id;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public abstract void work();
}package top.triabin._05exercise1;
/*
* 对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。
*/
public class Manager extends Employee {
private double bonus;//奖金
public Manager(double bonus) {
super();
this.bonus = bonus;
}
public Manager(String name,int id,double salary,double bonus) {
super(name,id,salary);
this.bonus = bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void work() {
System.out.println("管理员工,提高公司的运行效率");
}
}package top.triabin._05exercise1;
public class CommonEmployee extends Employee {
public void work() {
System.out.println("员工在一线车间生产产品");
}
}package top.triabin._05exercise1;
/*
* 请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。
*/
public class EmployeeTest {
public static void main(String[] args) {
Employee manager = new Manager("库克",1001,5000,50000);
manager.work();
CommonEmployee commonEmployee = new CommonEmployee();
commonEmployee.work();
}
}- 运行
编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,则将该雇员的工资增加100元。实验说明:
(1) 定义一个Employee类,该类包含:
- private成员变量name,number,birthday,其中birthday为MyDate类的对象;
- abstract方法earnings();
- toString()方法输出对象的name,number和birthday。
package top.triabin._05exercise2;
/*定义一个Employee类,该类包含:
- private成员变量name,number,birthday,其中birthday为MyDate类的对象;
abstract方法earnings();
toString()方法输出对象的name,number和birthday。
/
public abstract class Employee {private String name; private int number; private MyDate brithday; public Employee () {
super();
} public Employee(String name,int number,MyDate birthday) { this.name = name;
this.number = number;
this.brithday = 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 brithday;
}
public void setBrithday(MyDate brithday) {
this.brithday = brithday;
}
public abstract double earnings();
@Override
public String toString() {
return "[name=" + name + ", number=" + number + ", brithday=" + brithday.toDateString() + "]";
}
}
|
(3) 定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:
private成员变量monthlySalary;
实现父类的抽象方法earnings(),该方法返回monthlySalary值;
toString()方法输出员工类型信息及员工的name,number,birthday。
package top.triabin._05exercise2;
/*
* 定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:
* private成员变量monthlySalary;
* 实现父类的抽象方法earnings(),该方法返回monthlySalary值;
* toString()方法输出员工类型信息及员工的name,number,birthday。
*/
public class SalariedEmployee extends Employee{
private double monthlySalary;
public SalariedEmployee(String name,int number,MyDate birthday,double monthlySalary) {
super(name,number,birthday);
this.monthlySalary = monthlySalary;
}
public double earnings() {
return monthlySalary;
}
public String toString() {
return "SalariedEmployee " + super.toString();
}
public double getMonthlySalary() {
return monthlySalary;
}
public void setMonthlySalary(double monthlySalary) {
this.monthlySalary = monthlySalary;
}
}(4) 参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:
private成员变量wage和hour;
实现父类的抽象方法earnings(),该方法返回wage*hour值;
toString()方法输出员工类型信息及员工的name,number,birthday。
package top.triabin._05exercise2;
/*
* 参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:
* private成员变量wage和hour;
* 实现父类的抽象方法earnings(),该方法返回wage*hour值;
* toString()方法输出员工类型信息及员工的name,number,birthday。
*/
public class HourlyEmployee extends Employee {
private double hour;//月工作小时数
private double wage;//时薪
public HourlyEmployee(String name,int number,MyDate birthday,double wage,double hour) {
super(name,number,birthday);
this.wage = wage;
this.hour = hour;
}
public double getHour() {
return hour;
}
public void setHour(double hour) {
this.hour = hour;
}
public double getWage() {
return wage;
}
public void setWage(int wage) {
this.wage = wage;
}
public String toString() {
return "HourlyEmployee " + super.toString();
}
public double earnings() {
return wage * hour;
}
}(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);
package top.triabin._05exercise2;
import java.util.Calendar;
import java.util.Scanner;
/* 编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,
* 则将该雇员的工资增加100元。
*
* 定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。
* 利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday。
* 当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。
*/
public class PayrollSystem {
public static void main(String[] args) {
//方式一:控制台输入
// Scanner scan = new Scanner(System.in);
// System.out.print("请输入当前月份:");
// int month = scan.nextInt();
// scan.close();
//方式二:自动获取当前月份
Calendar calendar = Calendar.getInstance();
int month = calendar.get(Calendar.MONTH) + 1;//1月份为0
Employee[] emps = new Employee[3];
emps[0] = new SalariedEmployee("Triabin", 1001, new MyDate(1998,4,2),12000);
emps[1] = new HourlyEmployee("DawnLee", 1002, new MyDate(1998,8,4),60,240);
emps[2] = new SalariedEmployee("Trisolaran", 1003, new MyDate(1998, 4, 29) , 15000);
for(int i = 0;i < emps.length;i++) {
System.out.println(emps[i]);
System.out.println("月工资为:" + emps[i].earnings());
if(month == emps[i].getBirthday().getMonth()) {
System.out.println("生日快乐!奖励100元。");
}
}
}
}运行
接口(interface)
概述
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个”是不是”的关系,而接口实现则是 “能不能” 的关系。
接口的本质是契约,标准,规范,就像法律一样。指定好后大家都要遵守。
举例
接口的使用
使用interface来定义。
在Java语言中,接口和类是并列的两个结构。
如何定义接口:定义接口中的成员
① JDK7以前:只能定义全局常量和抽象方法
- 全局常量:public static final的,但是书写时可以省略不写
- 抽象方法:public abstract的
② JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法。(暂略)
接口中不能定义构造器,意味着接口不可以实例化。
Java开发中,接口通过让类去实现(implements)的方式来使用。
如果实现类覆盖了接口中所有抽象方法,则次实现类可以实例化;如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类。
Java类可以实现说个接口 —> 弥补了Java单继承性的局限性。
格式:class AA extends BB implements CC,DD,EE,…{} 先写继承,后写实现。
接口与接口之间可以继承,而且可以多继承。
接口的具体使用体现多态性。
接口实际上可以看做是一种规范。
package top.triabin._06interface;
/*
* 接口的使用
*/
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
System.out.println(Flyable.MIN_SPEED);
// Flyable.MIN_SPEED = 2;//The final field Flyable.MIN_SPEED cannot be assigned
Plane plane = new Plane();
plane.fly();
}
}
interface Flyable{
//全局常量
public static final int MAX_SPEED = 7900;//第一宇宙速度
int MIN_SPEED = 1;//省略了public static final
//抽象方法
public abstract void fly();
void stop();//省略了public abstract
// public Flyable() {
// //Interfaces cannot have constructors
// }
}
interface Attackable{
}
class Plane implements Flyable{
public void fly() {
System.out.println("通过引擎起飞");
}
public void stop() {
System.out.println("驾驶员减速停止");
}
}
abstract class Kite implements Flyable{
public void fly() {
// TODO Auto-generated method stub
}
}
class Bullet extends Object implements Flyable,Attackable,CC{
public void fly() {
// TODO Auto-generated method stub
}
public void stop() {
// TODO Auto-generated method stub
}
public void method1() {
// TODO Auto-generated method stub
}
public void method2() {
// TODO Auto-generated method stub
}
}
//************************************
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{
//多继承
}
面试题:抽象类与接口有哪些异同?
应用举例
-
package top.triabin._06interface;
/*
* 接口的使用
* 1. 接口使用上也满足多态。
* 2. 接口,实际上就是定义了一种多态。
* 3. 开发中,体会面向接口编程。
*/
public class USBTest {
public static void main(String[] args) {
Computer computer = new Computer();
//1. 创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
computer.transferData(flash);
System.out.println();
//2. 创建了接口的非匿名实现类的匿名对象
computer.transferData(new Printer());
System.out.println();
//3. 创建了接口的匿名实现类的非匿名对象
USB phone = new USB() {
public void start() {
System.out.println("手机开始工作");
}
public void stop() {
System.out.println("手机结束工作");
}
};
computer.transferData(phone);
//4. 创建了接口的匿名实现类的匿名对象
computer.transferData(new USB() {
public void start() {
System.out.println("mp3开始工作");
}
public void stop() {
System.out.println("mp3结束工作");
}
});
}
}
class Computer{
public void transferData(USB usb) {//USB usb = new Flash(); 接口的多态性
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大最小的传输速率等
void start();
void stop();
}
class Flash implements USB{
public void start() {
System.out.println("U盘开启工作");
}
public void stop() {
System.out.println("U盘结束工作");
}
}
class Printer implements USB{
public void start() {
System.out.println("打印机开启工作");
}
public void stop() {
System.out.println("打印机结束工作");
}
}
代理模式(Proxy)
概述:代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
package top.triabin._06interface;
/*
* 接口的应用:代理模式
*/
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse();
}
}
interface NetWork{
public void browse();
}
//被代理类
class Server implements NetWork{
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork{
private NetWork work;
public ProxyServer(NetWork work) {
this.work = work;
}
public void check(){
System.out.println("联网之前的一些检查工作");
}
public void browse() {
check();
work.browse();
}
}- 运行
应用场景:
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法调用(RMI)。
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
分类:
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
- JDK自动的动态代理,需要反射等知识
工厂设计模式
Java8中关于接口的改进
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法。
Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:使用static关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
默认方法:默认方法使用default关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。
代码演示
package top.triabin._06interface;
/*
* JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
*
*/
public interface CompareA {
//静态方法
public static void method1() {
System.out.println("CompareA:北京");
}
//默认方法
public default void method2() {
System.out.println("CompareA:上海");
}
default void method3() {//public可省略
System.out.println("CompareA:上海");
}
}package top.triabin._06interface;
public interface CompareB {
default void method3() {//public可省略
System.out.println("CompareB:上海");
}
}package top.triabin._06interface;
public class SuperClass {
public void method3() {
System.out.println("SuperClass:北京");
}
}package top.triabin._06interface;
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
// s.method1();
//知识点1:接口中定义的静态方法只能通过接口来调用。
CompareA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法
//实现类可以重写接口中的默认方法
s.method2();
//知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么
//子类在没有重写此方法的情况下,默认调用的是父类中同名同参数的默认方法。(类优先原则)
//知识点4:如果子类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现
//类没有重写此方法的情况下,报错。(接口冲突)
//这就需要我们在实现类中重写此方法
s.method3();
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
public void method2() {
System.out.println("SubClass:上海");
}
public void method3() {
System.out.println("SubClass:深圳");
}
//知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
public void myMethod() {
method3();//自己定义的重写方法
super.method3();//调用的是父类中声明的
//调用接口中默认的方法
CompareA.super.method3();
CompareB.super.method3();
}
}- 运行结果
练习题
面试题:排错1
interface A{
int x = 0;
}
class B{
int x = 1;
}
class C extends B implements A{
public void pX(){
System.out.println(x);//The field x is ambiguous.
//改正:
System.out.println(super.x);//调用父类中的x=1
System.out.println(A.x);//调用接口中的x=0
//如果不重名,可直接写相应名称即可
}
public static void main(String[] args){
new C().pX();
}
}面试题:排错2
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");//ball前面省略了public static final,是一个常量,不能再次赋值修改。
System.out.println(ball.getName());
}
}① 定义一个接口来实现两个对象的比较。
interface CompareObject{
public int compareTo(Object o);//若返回值是0,代表相等,代表当前对象大;负数代表当前对象小
}② 定义一个Circle类,声明radius属性,提供getter和setter方法。
package top.triabin._06exercise3;
/*
* 定义一个Circle类,声明radius属性,提供getter和setter方法。
*/
public class Circle {
private Double radius;
public Circle(Double radius) {
super();
this.radius = radius;
}
public Circle() {
super();
}
public Double getRadius() {
return radius;
}
public void setRadius(Double radius) {
this.radius = radius;
}
}③ 定义一个CompareableCircle类,继承Circle类并且实现CompareObject接口。在CompareableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
package top.triabin._06exercise3;
/*
* 定义一个CompareableCircle类,继承Circle类并且实现CompareObject接口。
* 在CompareableCircle类中给出接口中方法compareTo的实现体,用来比较两
* 个圆的半径大小。
*/
public class CompareableCircle extends Circle implements CompareObject{
public CompareableCircle(double radius){
super(radius);
}
public int compareTo(Object o) {
if(this == o) {
return 0;
}
if(o instanceof CompareableCircle) {
CompareableCircle c = (CompareableCircle)o;
//方式一:
// if(this.getRadius() > c.getRadius()) {
// return 1;
// }else if(this.getRadius() < c.getRadius()) {
// return -1;
// }else {
// return 0;
// }
//方式二:当属性radius声明为Double类型时,可以调用包装类方法
return this.getRadius().compareTo(c.getRadius());
}else {
return 0;
}
}
}
interface CompareObject{
public int compareTo(Object o);//若返回值是0,代表相等,正数代表当前对象大;负数代表当前对象小
}④ 定义一个测试类InterfaceTest,创建两个CompareableCircle对象,调用compareTo方法比较两个类的半径大小。
package top.triabin._06exercise3;
/*
* 定义一个测试类InterfaceTest,创建两个CompareableCircle对象,调用compareTo方法
* 比较两个类的半径大小。
*/
public class InterfaceTest {
public static void main(String[] args) {
CompareableCircle c1 = new CompareableCircle(3.4);
CompareableCircle c2 = new CompareableCircle(3.6);
int compareValue = c1.compareTo(c2);
if(compareValue > 0) {
System.out.println("c1半径比c2大");
}else if(compareValue < 0) {
System.out.println("c2半径比c1大");
}else if(compareValue == 0) {
System.out.println("c1和c2一样大");
}
}
}- 运行
⑤ 思考:参照上述做法定义矩形类Rectangle和CompareableRectangle类,在CompareableRectangle类中给出compareTo方法的实现,比较两个矩形的面积大小。
接口冲突的解决方式。
若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现:接口冲突。
- 解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
interface Filial{//孝顺的
default void help(){
System.out.println("老妈,我来救你了");
}
}
interface Spoony{//痴情的
default void help(){
System.out.println("媳妇,别怕,我来了");
}
}
class Man implements Filial,Spoony{
public void help(){
System.out.println("我该怎么办?");
Filial.super.help();
Spoony.super.help();
}
}
类的成员之五:内部类
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类。
内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)。
成员内部类:
一方面,作为外部类的成员:
- 调用外部类的结构
- 可以被static修饰
- 可以被4种不同的权限修饰
另一方面,作为一个类:
- 类内可以定义属性、方法、构造器等
- 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
- 可以被abstract修饰
关注如下3个问题:
① 如何实例化成员内部类
② 如何在成员内部类中区分调用外部类的结构
③ 开发中局部内部类的使用 见《InnerClassTest1.java》
fad
package top.triabin._07innerclass;
/*
* 类的内部成员之五:内部类
*/
public class InnerClassTest {
public static void main(String[] args) {
//创建Dog实例(静态的成员内部类):
Person.Dog dog = new Person.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类):
// Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
System.out.println();
bird.display("形参");
}
}
class Person{
String name = "外部类";
int age;
public void eat() {
System.out.println("人:吃饭");
}
static class Dog{
//静态成员内部类
String name;
int age;
public void show() {
System.out.println("卡拉是条狗");
}
}
class Bird{
//非静态成员内部类
String name = "内部类";
public void sing() {
System.out.println("唱歌");
Person.this.eat();//调用外部类的非静态属性
}
public void display(String name) {
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}
}
public void method() {
class AA{
//局部内部类
}
{
//局部内部类(代码块内)
}
}
public Person() {
class CC{
//局部内部类(构造器内)
}
}
}package top.triabin._07innerclass;
public class InnerClassTest1 {
//开发中很少见
public void method() {
//局部内部类
class AA{
}
}
}
章节练习及面试题
main()方法
选择题
class Test{ |
D:\>java Test Red Green Blue
what is the value of baz?
A. baz has value of “”
B. baz has value of null
C. baz has value of “Red”
D. baz has value of “Blue”
E. baz has value of “Green”
F. the code does not compile
G. the program throw an exception
接口
选择题
下面关于接口的说法中不正确的是()。
A.接口中所有的方法都是抽象的
B.接口中所有的方法都是public访问权限
C.子接口继承父接口所用的关键字是implements
D.接口是Java中的特殊类,包含常量和抽象方法
Java语言接口间的继承关系是()。
A.单继承 B.多重继承 C.不能继承 D.不一定
一个类实现接口的情况是()。
A.一次可以实现多个接口 B.一次只能实现一个接口
C.不能实现接口 D.不一定
填空题
- ____是声明接口的关键字,可以把它看成一个特殊类。接口中的数据成员默认的修饰符是_____,接口中的成员方法默认的修饰符是____。
- 如果实现某接口的类不是abstract的抽象类,则在类的定义部分必须该接口的所有抽象方法;如果实现某接口的类是abstract的抽象类,则它可以该接口所有的方法。但是对于这个抽象类任何一个非抽象的子类而言,它们父类所实现的接口中的所有抽象方法以及自身所实现接口中的抽象方法都必须有实在的。
补全代码
interface Inter { |
abstract class和interface有什么区别?
答:声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向某具体子类的一个实例。不能有抽象构造函数或抽象静态方法。abstract类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类。取而代之,在子类中实现该方法。知道其行为的其他类可以在类中实现这些方法。
接口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。
接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承实体类(concrete class)?
答:是,接口可继承接口。抽象类可以实现(implements)接口,抽象类可以继承实体类,但实体类必须不能是如下两种情况之一:①final修饰符修饰的类是不能的;②如果此实体类有且仅有私有的构造函数也是不能的。
是否能通过编译?
interface A{ |
答:不能。在编译时会发生错误,错误描述不同的JVM有不同的信息,意思就是未明确的x调用,两个x都匹配(就像同时import java.util和java.sql两个包时直接声明Date一样)。