1.关键字:static
1.1类属性(静态变量)和类方法(静态方法)的设计思想
类属性(静态变量)与类方法(静态方法):作为该类的各个对象之间共享的变量。在设计类时,那些不因对象的不同而改变的属性(方法)可设置为类属性(方法)。由于不需要创建对象就可以调用静态方法,简化的方法的调用。调用格式为:类名.方法名(形参列表)。
1.2static的使用范围
属性,方法,代码块,内部类。注意:构造器不可以。
1.3static修饰属性
<1>使用static修饰属性→静态变量(或称类变量,类属性)
<2>按是否有static修饰,属性分为静态属性和非静态属性(或称实例变量)
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象a修改静态变量后,其他对象在调用此静态变量时,此静态变量的值为对象a修改后的值。
实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,其他对象中同样属性的属性值不会改变。
静态变量 | 实例变量 | |
类 | 可以调用 | 不可以调用 |
对象 | 可以调用 | 可以调用 |
<3>静态变量是随着类的加载而加载,其加载要早于对象的创建;且由于类只会加载一次,所以静态变量在内存中也只会存在一份→存在方法区的静态域内。
<4>访问权限允许时,可不创建对象,直接被调用:类名.静态变量名。
<5>Java中,static不可以用来修饰局部变量。
1.4static修饰方法
<1>使用static修饰方法→静态方法(或称类方法)
<2>静态方法随着类的加载而加载,可通过“类名.静态方法名()”的方式进行调用
<3>
静态方法 | 非静态方法 | |
类 | 可以调用 | 不可以调用 |
对象 | 可以调用 | 可以调用 |
<4>静态方法中,只能调用静态的方法或属性(可从类和对象的生命周期角度思考);
非静态方法中,既可以调用非静态的方法和属性,也可以调用静态的方法和属性。
1.5static使用的注意点
<1>在静态的方法内,不能使用this关键字,super关键字;
<2>static修饰的方法不能被重写。
1.6何时将属性和方法声明为static的
<1>对于属性,若某属性是可以被多个对象所共享的,不会随着对象的不同而不同;
<2>对于方法,1)操作静态属性的方法,通常设置为static的,2)工具类中的方法,习惯上声明为static的。
1.7简单举例
public class Entity //Entity类
{
private int id; //实体的编号
private static int total; //静态属性,总的实体的个数
private static int init; //静态属性,实体的编号要递增
public Entity() //构造器
{
id=init++;
total++;
}
public static int getTotal() //静态方法,返回总的实体个数
{
return total;
}
}
public class EntityTest
{
public static void main(String[] args)
{
Test a=new Entity();
Test b=new Entity();
System.out.println(b.getTotal()); //2
System.out.println(Test.getTotal()); //也是2
}
}
2.单例设计模型(Singleton)
2.1设计模式
是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
2.2单例模型是什么以及实现方法
类的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类提供一个取得其对象实例的方法。
要想让类在一个虚拟机中只产生一个对象,1)首先将类的构造器访问权限设置为private,这就无法在类外部产生该类的对象;2)再在类内部new一个当前类的实例;3)调用该类的静态方法返回类内部创建的对象。注意,由于静态方法只能访问类中的静态变量,所以指向类内部产生该类对象的变量也必须定义为静态的。
2.3代码实现
<1>饿汉式
public class Singleton //单例模式,饿汉式
{
private Singleton () //声明构造器为private
{}
//声明当前类的对象,且为static
private static Singleton singleton=new Singleton();
//声明public static 返回当前类对象的方法
public static Singleton getSingleton()
{
return singleton;
}
}
<2>懒汉式
public class Singleton //单例模式,懒汉式
{
private Singleton()
{}
private static Singleton singleton=null;
public static Singleton getSingleton()
{
if(singleton==null)
singleton=new Singleton();
return singleton;
}
}
饿汉式与懒汉式的对比:
饿汉式:好处:是线程安全的;坏处:对象加载时间过长
懒汉式:好处:延迟对象的创建;坏处:线程不安全(当前写法不安全,后面学了多线程再改)
2.4单例模式的优点
单例模式只生成一个实例,减少了系统的开销,当一个对象的产生需要较多的资源时,如读取配置,产生其他依赖对象时,则可以通过再应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
2.5单例模式的应用场景
3.类的成员之四——代码块
3.1代码块的作用
用来初始化类、对象的信息
3.2代码块的修饰符
代码块如果使用修饰符,也只能使用static,所以有:静态代码块和非静态代码块
3.3静态代码块
<1>内部可有输出语句;
<2>随着类的加载而加载,而且只执行一次;
<3>作用:初始化类的信息;
<4>如果一个类中定义了多个静态代码块,则按声明的先后顺序执行;
<5>静态代码块优先于非静态代码块的执行;
<6>静态代码块内只能调用静态的属性、方法,不能调用非静态的。
3.4非静态代码块
<1>内部可有输出语句;
<2>随着对象的创建而执行,每创建一个对象就执行一次;
<3>作用:可在创建对象时,对对象的属性进行初始化;
<4>非静态代码块内可以调用静态和非静态的属性、方法。
3.5关于代码块的一些注意事项
<1>非静态代码块的执行先于构造器;
<2>对于存在父类,存在静态代码块,存在非静态代码块的程序,要由父及子,静态先行;
<3>在类中,对于属性的显式初始化和代码块,按照代码顺序的前后依次执行。
3.6一个简单举例
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();
System.out.println("Mid的带参数构造器,其参数值:"+ msg);
}
}
class Leaf extends Mid
{
static
{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
super("学Java");
System.out.println("Leaf的构造器");
}
}
public class LeafTest
{
public static void main(String[] args)
{
new Leaf();
System.out.println("**************************");
new Leaf();
}
}
// 输出结果如下:
// Root的静态初始化块
// Mid的静态初始化块
// Leaf的静态初始化块
// Root的普通初始化块
// Root的无参数的构造器
// Mid的普通初始化块
// Mid的无参数的构造器
// Mid的带参数构造器,其参数值:学Java
// Leaf的普通初始化块
// Leaf的构造器
// **************************
// Root的普通初始化块
// Root的无参数的构造器
// Mid的普通初始化块
// Mid的无参数的构造器
// Mid的带参数构造器,其参数值:学Java
// Leaf的普通初始化块
// Leaf的构造器
4.关键字:final
4.1final可修饰的结构
类,方法,变量
<1>final修饰类:此类就不能被其他类所继承 (格式:权限修饰符 final class A{ } )
eg:String类,System类,StringBuffer类
<2>final修饰方法:此方法就不能被重写 (格式:权限修饰符 final 返回值类型 B() {})
eg:Object类中的getClass(方法)
<3>final修饰变量:此时“变量”称为是一个常量,常量名需全部大写 (final 变量类型 a=10)
注意:当使用final修饰形参时,表明此形参是一个常量,当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能重新赋值。
4.2全局常量:static final
5.抽象类与抽象方法
5.1关键字:abstract
<1>abstract可以用来修饰的结构:类,方法
<2>abstract修饰类→抽象类
1)这个类不能被实例化;
2)抽象类中一定有构造器(即使没有显示的声明),便于子类实例化时调用;
3)开发中都会提供抽象类的子类,让子类对象实例化。
<3>abstract修饰方法→抽象方法
1)抽象方法只有方法的声明,没有方法体
eg:public abstract void eat();
2)包含抽象方法的类,一定是一个抽象类;反之,抽象类可以没有抽象方法;
3)若子类重写了父类中所有的抽象方法后,此子类方可实例化;只要存在有一个父类中的抽象方法没有被子类重写,这个子类就仍是一个抽象类。
<4>abstract不能用来修饰私有方法,静态方法,final的方法,final的类
5.2抽象类的匿名类
代码展示:
抽象类:Person类,含有两个抽象方法eat()和walk()
public abstract class Person
{
int age;
String name;
public abstract void eat();
public abstract void walk();
}
public class PersonTest
{
public static void main(String[] args)
{
//创建了一个匿名子类的对象:p
Person p=new Person()//这里还必须写成new Person()的形式
{
//里面要对Person类中所有的抽象方法进行重写
@Override
public void eat()
{
System.out.println("人吃饭");
}
@Override
public void walk()
{
System.out.println("人走路");
}
};//这里还需要加个分号
method(p);
//方法二
// //方法一还创建匿名子类的匿名对象,前面的代码还有对象名p,方法二这里对象名都没有
// method(new Person()
// {
// @Override
// public void eat()
// {
// System.out.println("人吃饭");
// }
// @Override
// public void walk()
// {
// System.out.println("人走路");
// }
// });
}
public static void method(Person p)
{
p.eat();
p.walk();
}
}
5.3模板方法设计模式(TemplateMethod)
<1>解决的问题:当功能内部一部分实现是确定的,一部分实现是不确定的,这时可以把不确定的部分暴露出去,让子类去实现。
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现,这就是一种模板模式。
<2>举例说明:代码展示
abstract class Template
{
//计算某段代码执行所需要花费的时间
public void spendTime()
{
long start = System.currentTimeMillis();
this.code();//不确定的部分、易变的部分
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
}
public abstract void code(); //不确定的部分,声明为abstract方法,让子类去实现
}
class SubTemplate extends Template
{
@Override
public void code() //一段输出1000以内质数的代码
{
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.println(i);
}
}
}
}
public class Test
{
public static void main(String[] args)
{
SubTemplate a=new SubTemplate();
a.spendTime();
}
}
5.4一个简单例题
编写工资系统,实现不同类型员工(多态)的按月发放工资,如果当月出现某个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。 4)定义HourlyEmployee类,实现按小时计算工资的 员工处理。该类包括:private成员变量wage和hour;实现父类的抽象方法earnings(),该方法返回wage*hour值;toString()方法输出员工类型信息及员工的name,number,birthday。 5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类 型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。
代码展示:
MyDate类:
public class MyDate
{
private int year;
private int month;
private int day;
/**
构造器
*/
public MyDate(int year,int month,int day)
{
this.year=year;
this.month=month;
this.day=day;
}
/**
输出年月日
*/
public String toDateString()
{
return year+"年 "+month+"月 "+day+"日 ";
}
public int getMonth()
{
return month;
}
}
Emplyee类:
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 abstract double earnings();
/**
重写toString方法
*/
@Override
public String toString()
{
return "Employee{" +
"name='" + name + '\'' +
", number=" + number +
", birthday=" + birthday.toDateString() +
'}';
}
public MyDate getBirthday()
{
return birthday;
}
public String getName()
{
return name;
}
public int getNumber()
{
return number;
}
}
SalariedEmployee类:
public class SalariedEmployee extends Employee
{
private double monthlySalary;
public SalariedEmployee(String name,int num,MyDate birthday,double monthlySalary)
{
super(name,num,birthday);
this.monthlySalary=monthlySalary;
}
@Override
public double earnings()
{
return monthlySalary;
}
@Override
public String toString()
{
return "SalariedEmployee["+super.toString()+"]";
}
}
HourlyEmployee类:
public class HourlyEmployee extends Employee
{
private double wage;
private double hour;
public HourlyEmployee(String name,int num,MyDate birthday,double wage,double hour)
{
super(name,num,birthday);
this.wage=wage;
this.hour=hour;
}
@Override
public double earnings()
{
return wage*hour;
}
@Override
public String toString()
{
return "HourlyEmployee["+super.toString()+"]";
}
}
PayrollSystem类:
public class PayrollSystem
{
public static void main(String[] args)
{
Employee[] emps=new Employee[]{
new SalariedEmployee("Tom",1,new MyDate(1998,12,3),1900),
new SalariedEmployee("Jay",2,new MyDate(1997,1,23),2000),
new SalariedEmployee("Lei",3,new MyDate(1996,5,12),1950),
new HourlyEmployee("Eason",4,new MyDate(1999,10,12),90,12)};
for(int i=0;i<emps.length;i++)
{
System.out.println(emps[i].toString());
if(emps[i] instanceof SalariedEmployee && emps[i].getBirthday().getMonth()==5)//5月生日的员工工资加100
System.out.println("员工"+emps[i].getName()+"本月工资额外加100");
}
}
}
6.接口(interface)
6.1接口的使用
<1>接口使用interface来定义;
<2>Java中,接口和类是并列的两个结构;
<3>如何定义接口,定义接口中的成员:
1)jdk7之前,只能在接口内定义全局变量和抽象方法
全局变量:public static final (具体code时,可省略public static final,因为系统会自动默认)
抽象方法:public abstract (具体code时,可省略public abstract)
2)jdk8:除了定义全局常量和抽象方法外,还可以定义静态方法和默认方法
<4>接口不能定义构造器:意味着接口无法实例化
<5>接口通过让类去实现(implements)的方式来使用
eg:class Plane implements Flyable {}
若实现类覆盖了接口中的所有抽象方法,则此实现类就可实例化;
若实现类没有覆盖接口中的所有抽象方法,则此实现类仍为一个抽象类。
<6>Java类可以实现多个接口→弥补了Java单继承的局限性
格式: class AA extends BB implements CC,DD,EE
<7>接口与接口之间可以继承,且可以多继承
格式:interface AA extends BB,CC
<8>接口的具体使用,体现了多态性
<9>创建接口实现类的匿名对象
针对<8>和<9>,有个简单举例,见代码如下:
public class InterfaceTest
{
public static void main(String[] args)
{
Computer computer=new Computer();
Flash flash=new Flash();
computer.transferDate(flash);
//下面代码为非匿名实现类的匿名对象
//computer.transferDate(new Flash());
//下面代码为匿名实现类的非匿名对象
//USB usb=new USB(){
// public void start()
// {
// System.out.println("start");
// }
// public void stop()
// {
// System.out.println("stop");
// }
// }
//computer.transferDate(usb);
//下面的代码为匿名实现类的匿名对象的代码
// computer.transferDate(new USB(){
// public void start()
// {
// System.out.println("start");
// }
// public void stop()
// {
// System.out.println("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("打印机停止工作");
}
}
class Computer
{
public void transferDate(USB usb)
{
usb.start();
System.out.println("数据传输中.....................");
usb.stop();
}
}
<10>接口上实际上是一种规范
<11>开发中,体会面向接口编程
6.2接口应用之——代理模式
代理设计:就是为其它对象提供一种代理以控制对这个对象的访问。
代码展示:这个ProxyServer就是Server的代理,通过ProxyServer来访问Server对象。
public class ProxyTest
{
public static void main(String[] args)
{
Server server=new Server();
ProxyServer pro=new ProxyServer(server);
pro.brower();
}
}
interface Network
{
public abstract void brower();
}
class Server implements Network
{
public void brower()
{
System.out.println("真实服务器的浏览");
}
}
class ProxyServer implements Network
{
private Network network; //这一步和下一步是关键
public ProxyServer(Network network)
{
this.network=network;
}
public void check()
{
System.out.println("做好网络检查");
}
public void brower()
{
check();
network.brower();
}
}
6.3接口应用之——工厂模式
6.4jdk8中接口的新特性
接口中还可以定义静态方法和默认方法
<1>格式:
静态方法:public static 权限修饰符 AA { }
默认方法:public default 权限修饰符 BB { }
<2>一些说明:
1)接口中定义的静态方法,只能通过接口来调用;
2)接口中定义的默认方法,通过实现类的对象来调用;
3)如果实现类(假如存在父类)继承的父类和实现的接口中声明了同名同参的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参的方法。称作类优先原则(属性不可以)。
4)如果实现类实现了多个接口,而且这多个接口定义了同名同参的默认方法,那么在实现类没有重写此方法的情况下,将会报错。称作接口冲突(这就需要我们必须在实现类中重写此方法)
5)如果在实现类的方法中调用接口中被重写的默认方法,使用:接口名.super.方法名(),对于静态方法依旧为,使用:接口名.方法名()
<3>代码展示:
CompareA接口:
public interface CompareA
{
public static void method1() //静态方法
{
System.out.println("CompareA:北京");
}
public default void method2() //默认方法
{
System.out.println("CompareA:上海");
}
default int method3() //默认方法
{
return 3;
}
}
以下为基于该接口的其他代码:
public class SubclassTest
{
public static void main(String[] args)
{
Subclass a=new Subclass();
CompareA.method1();
//a.method1(); 这样错误,因为接口中的静态方法,只能通过接口调用
System.out.println(a.method3()); //返回的是10,因为类优先原则
a.show();
}
}
class Adf
{
public int method3()
{
return 10;
}
}
class Subclass extends Adf implements CompareA
{
public void show()
{
CompareA.super.method2();
}
}
//输出结果为:
//CompareA:北京
//10
//CompareA:上海
7.类的成员之无——内部类
7.1定义
Java允许将一个类A声明在一个类B中,则类A就是内部类,类B为外部类。
7.2内部类的分类
<1>成员内部类(又可分为静态成员内部类,非静态成员内部类)
<2>局部内部类(定义在方法内、代码块内、构造器内)
注意:成员内部类和局部内部类编译后都会生成字节码文件,其中,成员内部类:外部类$内部类名.class;局部内部类:外部类$数字 内部类名.class
7.3成员内部类
<1>成员内部类的理解
▲一方面,作为外部类的成员
1)可以调用外部类的结构;
2)可以被static修饰,此时就不能在调用外部类中非static的成员变量;
3)可以被四种权限修饰符所修饰;
▲另一方面,作为一个类
1)类内可以定义属性,方法,构造器
2)可以被final修饰,表示此类不能被继承,也就是说不使用final,就可以被继承;
3)可以被abstract修饰;
<2>一些注意事项:
1)非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员;
2)外部类访问成员内部类的成员,需要”内部类.成员(对静态成员内部类而言)“或”内部类对象.成员(对非静态成员内部类而言)“的方式;
3)成员内部类可以直接使用外部类的所有成员,包括私有的数据;
4)当想要在外部类的静态成员部分使用内部类时,可以考虑将内部类声明为静态的。
<3>成员内部类的使用
1)如何创建成员内部类的对象(以类Person为例,其含有一个静态内部类Brain,一个非静态内部类Hands)
▲创建静态的Brain内部类的实例(静态的成员内部类的对象)
Person.Brain brain=new Person.Brain();
▲创建非静态的Hands内部类的实例(非静态的成员内部类的对象)
Person p=new Person();
Person.Hands hands=p.new Hands();
2)如何在成员内部类中调用外部类的结构
class Animal
{
String name = "小白";
public void eat()
{
System.out.println("动物都吃东西");
}
//非静态成员内部类
class Bird
{
String name = "杜鹃";
public void display(String name)
{
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Animal.this.name);//外部类的属性(注意这里的格式)
Person.this.eat();//也可以直接写成eat(),前提是内部类中没有重名的方法
}
}
}
public class InnerclassTest
{
public static void main(String[] args)
{
Animal animal=new Animal();
Animal.Bird bird=animal.new Bird();
bird.display("黄鹂");
}
//输出结果为:
//黄鹂
//杜鹃
//小白
//动物都吃东西
7.4局部内部类的使用
1)只能在类的方法和代码块中声明;
2)与局部变量类似,不能使用四种权限修饰符,不能用static修饰,因此不能包含静态成员;
3)在局部内部类的方法中(比如show()),如果调用局部内部类所在的方法(比如method())中的局部变量的值,要求此局部变量声明为final(jdk8之后,final可省略,默认为常量);
public void method
{
int num=10;//默认为final
class AA //局部内部类
{
public void show()
{
System.out.println("show");
}
}
4)开发中一般这样用,局部内部类的对象可以通过外部方法的返回值返回使用,返回值类型设置为局部内部类的父类或者父接口类型,如下:
public Comparable getComparable() //这个Comparable是一个接口类型
{
class MyComparable implements Comparable
{
@override
public int compareTo(Object o)
{
............
}
}
return new MyComparable
}