关键字:static
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上 的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象, 其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少 对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个 国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中 都单独分配一个用于代表国家名称的变量。
static可以修饰属性、方法、代码块、内部类
使用static修饰属性:静态变量/类变量
属性,按是否使用static修饰,又分为静态属性&非静态属性(局部变量)
实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性修改。
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过的。
说明:
①静态变量随着类的加载而加载,可以通过“类.静态变量”的方式进行调用
②静态变量的加载要早于对象的创建
③由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中
④ 类变量 实例变量
类 yes no
对象 yes yes
静态属性举例:System.out,Math.PI
在开发中如何确定一个属性是否要声明为static的?
> 属性是可以被多个对象共享的,不会随着对象的不同而不同
> 类中的常量也常常声明为static的
使用static修饰方法:静态方法
①随着类的加载而加载,可以通过“类.静态方法”的方式调用
② 静态方法 非静态方法
类 yes no
对象 yes yes
③静态方法中,只能调用静态的方法和属性;非静态方法中,既可以调用静态的属性和方法,也可以调用非静态的属性和方法
④在静态的方法内,不能使用this、super关键字。静态属性和方法前省略的是类名.属性,类名.方法。应当从生命周期的角度去理解。
在开发中如何确定一个方法是否要生命为static的?
> 操作静态属性的方法,通常设置为static的,如get、set方法
> 工具类的方法,习惯上声明为static的。比如Math、Arrays、Collections
public class CircleTest{
public static void main(String[] args){
Circle c1=new Circle();
Circle c2=new Circle();
System.out.println("c1的ID:"+c1.getId());//c1的ID:1001
System.out.println("c2的ID:"+c2.getId());//c2的ID:1002
System.out.println("创建圆的个数为:"+Circle.getTotal());//创建圆的个数为:2
}
}
class Circle{
private double redius;
private int id;
private static int total;
private static int init=1001;
//构造器
public Circle(){
id=init++;//实现自增长
total++;
}
public Circle(){
this();
this.radius=radius;
}
//方法
public double findArea(){
return 3.14*radius*radius;
}
//get、set方法
public double getRedius(){
return redius;
}
public void setRedius(double redius){
this.redius=redius;
}
public int getId(){
return id;
}
public static int getTotal(){
return total;
}
}
内存解析:
单例(Singleton)设计模式
设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模免去我们自己再思考和摸索。就像是经典 的棋谱,不同的棋局,我们用不同的棋谱。”套路”
常用的设计模式有 --- 23种经典设计模式
创新型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
《设计模式》1995,由“四人组(Gang of Four)”合著
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对 某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象, 静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
实现方法:饿汉式/懒汉式
public clss SingletonTest1{
public static void main(Sring[] args){
Bank bank1=Bank.getInstance();
Bank bank2=Bank.getInstance();
}
}
//饿汉式
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象,相当于类的一个属性
//4.要求此对象也必须是静态的
private static Bank instance = new Bank();
//3.提供公共的静态的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
//懒汉式
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;
}
}
//一种简化的饿汉式
class Bank{
private Bank(){
}
public static final Bank instance=new Bank();
}
区分饿汉式和懒汉式
饿汉式:缺点:对象加载时间过长;优点:线程安全
懒汉式:优点:延迟对象的创建;缺点:目前的写法点成不安全--->到多线程时再修改
单例模式的优点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
典型的单例: java.lang.Runtime
应用场景
网站的计数器,一般也是单例模式实现,否则难以同步。
应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志 文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库 资源。
项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置 文件数据,都生成一个对象去读取。
Application 也是单例的典型应用
Windows的Task Manager (任务管理器)就是很典型的单例模式
Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程 中,回收站一直维护着仅有的一个实例。
理解main方法的语法
public static void main(String[] args){//方法体}
- main方法作为程序的入口
- main方法也是一个普通的静态方法
- main方法可以作为我们与控制台交互的方式(之前:使用Scanner)
由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。
又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到。
命令行参数用法举例
public class CommandPara {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "] = " + args[i]);
}
}
}
//运行程序CommandPara.java,后面跟参数列表,可加双引号也可以不加,都认为是String类型的参数。
/*
* javac CommandPara.java
* java CommandPara Tom Jerry "Shkstart"
*/
/*
* 输出结果:
* args[0] = Tom
* args[1] = Jerry
* args[2] = Shkstart
*/
类和类的成员之四:代码块(或初始化块)
代码块的作用:用来初始化类、对象
代码块如果有修饰的话,只能使用static。由是否被static修饰,分为静态代码块和非静态代码块
代码块内部可以有输出语句
静态代码块随着类的加载而执行,而且只执行一次;非静态代码块对着对象的创建而执行,每创建一个对象,就执行一次非静态代码块
作用:静态代码块初始化类的信息,非静态代码块在创建对象时对对象属性进行初始化
一个类中可以定义多个静态/非静态代码块,按声明的先后顺序执行。一般不造多个代码块。
非静态的属性和方法可以在静态和非静态的代码块中调用,静态的属性和方法只能在静态代码块中调用。
属性赋值的先后顺序(完结)
①默认初始化
②显示初始化 / ⑤在代码块中赋值
③构造器初始化
④有了对象以后,通过“对象.属性”,“对象.方法“的方式,进行赋值
例子:由父及子,静态现行
package com.atguigu.java;
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:"
+ msg);
}
}
class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("尚硅谷");
System.out.println("Leaf的构造器");
}
}
public class LeafTest{
public static void main(String[] args){
new Leaf();
//new Leaf();
}
}
/*
输出:
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
Leaf的构造器
*/
关键字:final
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。
final标记的类:不能被继承。提高安全性,提高程序的可读性。比如:String类、System类、StringBuffer类
final标记的方法:不能被子类重写。比如:Object类中的getClass()。
final标记的变量:(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。
final修饰属性:可以考虑赋值的位置有:显示初始化、代码块中初始化、构造器中初始化
final修饰局部变量:尤其是final修饰形参时,表明此形参是个常量,调用此方法时给形参赋值,此后只能在方法体内使用该形参,不能赋值。(引用型变量是否可以赋值?)
static final用来修饰属性:全局常量
在开发中,通常对于自己定义的方法不适用final,对于常量则会使用final
关键字:native*
调用底层的C或C++代码来实现,不会用java来实现,因此不会对外开放
public final native Class<?> getClass;
抽象类与抽象方法(关键字:abstract)
抽象类:随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
> 抽象类不能实例化
> 抽象类中一定有构造器,便于子类实例化时调用
> 开发中,都会提供抽象类的子类,让子类实例化,完成相关操作
抽象方法:public abstract void eat();
> 只有方法的声明,没有方法体
> 包含抽象方法的类一定是一个抽象类,反之抽象类中可以没有抽象方法
> 若子类重写了父类的抽象方法,此子类才可以实例化
若子类没有重写父类中的所有抽象方法,则此子类也应是一个抽象类
abstract使用上的注意点:
abstract不能用来修饰:属性、构造器等结构
abstract不能用来修饰private私有方法、static静态方法、final的方法、final的类
匿名子类
public static void main(String args[]){
//创建了一匿名子类的对象:p
Person p=new Person(){
//如果子类只用一次,则不需要单独创建一个子类,直接使用匿名子类,名字仍用父类的类名
//需要将父类中的抽象方法全部重写
@Override
public void eat(){
System.out.println("吃东西");
}
@Override
public void breath(){
System.out.println("好好呼吸");
}
}
method1(p);
System.out.println("********************")
//创建匿名子类的匿名对象
method1(new Person(){
@Override
public void eat(){
System.out.println("吃东西");
}
@Override
public void breath(){
System.out.println("好好呼吸");
}
});
}
public static void method1(Person p){//在抽象类Person中的eat和breath方法都是抽象方法。
p.eat();
p.breath();
}
模板方法设计模式(TemplateMethod)
解决的问题:当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用, 这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
例子:
public class TemplateTest{
public static void main(String[] args){
SubTemplate t=new SubTemplate();
t.getTime();
}
}
abstract class Template {
//计算某段代码花费的时间
public final void getTime() {
long start = System.currentTimeMillis();
code();//不确定的部分、易变的部分
long end = System.currentTimeMillis();
System.out.println("执行时间是:" + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template {
@Override
public void code() {
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
}
}
例题:
package com.atguigu.java;
//抽象类的应用:模板方法的设计模式
public class TemplateMethodTest {
public static void main(String[] args) {
BankTemplateMethod btm = new DrawMoney();
btm.process();
BankTemplateMethod btm2 = new ManageMoney();
btm2.process();
}
}
abstract class BankTemplateMethod {
// 具体方法
public void takeNumber() {
System.out.println("取号排队");
}
public abstract void transact(); // 办理具体的业务 //钩子方法
public void evaluate() {
System.out.println("反馈评分");
}
// 模板方法,把基本操作组合到一起,子类一般不能重写
public final void process() {
this.takeNumber();
this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码
this.evaluate();
}
}
class DrawMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要取款!!!");
}
}
class ManageMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要理财!我这里有2000万美元!!");
}
}
模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:
- 数据库访问的封装
- Junit单元测试
- JavaWeb的Servlet中关于doGet/doPost方法调用
- Hibernate中模板程序
- Spring中JDBCTemlate、HibernateTemplate等
接口(interface)
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方 法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打 印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则 必须能...”的思想。**继承是一个"是不是"的关系,而接口实现则是 "能不能" 的关系。 **
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都 要遵守。
接口的使用:
1、接口使用interface来定义。
2、在Java中,接口和类是并列的两个结构。
3、如何定义接口:接口中的成员
JDK7及以前:只能定义全局常量和抽象方法
> 全局常量:public static final的,但是书写中可以省略不写
> 抽象方法:public abstract的
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)
interface Flyable{
//全局常量
public static final int MAX_SPEED=7900;
int MIN_SPEED=1;//省略了public static final
//抽象方法
public abstract void fly();
void stop();//省略了public abstract
}
4、接口中不能定义构造器!意味着接口不可以实例化
5、Java开发中,接口通过让类去实现(inpements)的方式来使用
如果实现类实现了接口中的所有抽象方法,则此类就可以实例化,否则此类认为一个抽象类
6、Java类可以实现多个接口--->弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE
//Flyable接口见上文
interface Atteckable{
void attack();
}
class Plane implements Flyable{
@Override
public void fly(){
System.out.println("通过引擎起飞");
}
@Override
public void stop(){
System.out.println("驾驶员减速停止");
}
}
class Bullet extends Object implements Flyable,Attackable{
@Override
public void attack(){}
@Override
public void fly(){}
@Override
public void stop(){}
}
7、接口和接口之间可以继承,而且可以多继承
interface AA{
}
interface BB{
}
interface CC extends AA,BB{
}
8、接口的使用,体现多态性
9、接口,实际上可以看做是一种规范
项目的具体需求是多变的,我们必须以不变应万变才能从容开发。此处的“不变”就是“规范”。因此,我们开发大项目往往都是“面向接口编程”。
//体验多态性和规范
public class USBTest{
public static void main(String[] args){
Computer com=new Comtuper();
//1.创建接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferData(flash);
//2.创建接口的非匿名实现类的匿名对象
com.transferData(new Printer());
//3.创建接口的匿名实现类的非匿名对象
USB phone=new USB(){
@Override
public void start(){
System.out.println("手机开始工作");
}
@Override
public void stop(){
System.out.println("手机结束工作");
}
};
com.transferData(phone);
//4.创建接口的匿名实现类的匿名对象
com.transferData(new USB(){
@Override
public void start(){
System.out.println("mp3开始工作");
}
@Override
public void stop(){
System.out.println("mp3结束工作");
}
});
}
}
class Computer{
public void transferDta(USB usb){//USB sub=new Flash
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
class Flash implements USB{
@Override
public void start(){
System.out.println("U盘开启工作");
}
@Override
public void end(){
System.out.println("U盘结束工作");
}
}
class Printer implements USB{
@Override
public void start(){
System.out.println("打印机开启工作");
}
@Override
public void end(){
System.out.println("打印机结束工作");
}
}
应用举例:JDBC接口
接口的应用:代理模式
概述: 代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
应用场景:
安全代理:屏蔽对真实角色的直接访问
远程代理:通过代理类处理远程方法调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有 100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理 模式,当需要查看图片时,用proxy来进行大图片的打开。
分类
静态代理(静态定义代理类) 针对每一套接口都需要定义一套代理类
动态代理(动态生成代理类) JDK自带的动态代理,需要反射等知识
public class NetWorkTest{
public static void main(String[] args){
//造代理类的对象,把被代理类传进去
Server server=new Server();
ProxyServer proxyServer=new ProxyServer();
proxyServer.browse();
}
}
interface NetWork{
publiv void browse();
}
//被代理类
class Server implements NetWork{
@Override
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("联网之前的检查工作");
}
@Override
public void browse(){
check();
work.browse();
}
}
工厂设计模式
包括两种设计模式:工厂方法模式、抽象工厂模式
详见 : 《拓展:工厂设计模式.doc》
Java8中接口的改进
Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。实现类继承不到该方法,也不能使用该方法,“完全无视”。
我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。 如果实现类如果重写了接口中的默认方法,调用时调用的是重写的方法。
如果类继承的父类和实现的接口中,声明了同名同参数的默认方法,那么在没有重写此方法的情况下,默认调用的是父类中的方法。(属性则不适用,如果调用父类和接口中同名同参数的属性,则编译不通过。所以必须用"super."和"接口."显示的区分)--->类优先原则
如果实现类实现了多个接口,而这多个接口中声明了同名同参数的默认方法,那么在子类没有重写此方法的情况下,报错。(如果重写,则同时覆盖多个接口中的方法)--->接口冲突
我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。 比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认 方法。
public static void method1(){
}
public default void method2(){
}
//其中public都可以省略,省略后仍是public的
如何在子类或实现类的方法中,调用父类、接口中被重写的方法?
//调用自己定义的重写的方法
method3();
//调用父类中声明的方法
super.method3();
//调用接口中的默认方法
CompareA.super.method3();//接口名.super.方法名
练习:接口冲突的解决方式(一个绝绝子的例子)
interface Filial {// 孝顺的
default void help() {
System.out.println("老妈,我来救你了");
}
}
interface Spoony {// 痴情的
default void help() {
System.out.println("媳妇,别怕,我来了");
}
}
class Man implements Filial, Spoony {
@Override
public void help() {
System.out.println("我该怎么办呢?");
Filial.super.help();
Spoony.super.help();
}
}
类的成员之五: 内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内 部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使 用内部类。
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类。
内部类的分类:成员内部类(静态、非静态) vs. 局部内部类(方法内、代码块内、构造器内)
//内部类如果要调用外部类的非静态属性,可以直接调用,或
Person.this.eat();//外部类.this.外部类方法
//内部类方法内调用属性
//方法的形参
System.out.println(name);
//内部类的属性
System.out.println(this.name);
//外部类的属性
System.out.println(Person.this.name);
成员内部类:
一方面,作为外部类的成员:
> 调用外部类的结构
> 可以被static修饰
> 可以被4种不同的权限修饰
另一方面,作为一个类:
> 类内可以定义属性、方法、构造器等
> 可以被final修饰,表示此类不能被继承
> 可以被abstract修饰
ps.别忘了,外部类不能被static修饰(static只修饰类内部的成员,非构造器),内部类可以被static修饰;外部类不能被全部4种权限修饰
关注如下3个问题
- 如何实例化成员内部类的对象
public static void main(String[] args){
//创建静态的成员内部类
Person.Dog dog=new Person.Dog();
dog.show();//内部类的方法
//创建非静态的成员内部类
Person p=new Person();
Person.Bird bird=p.new Bird();
}
- 如何在成员内部类中调用外部类的结构
- 开发中局部内部类的使用
//返回一个实现了Comparable接口的类的对象
//方式一
public Comparable getComparable(){
//创建一个实现了Comparable接口的类:局部内部类
class MyComparable implements Comparable{
@Override
public int compareTo(Object o){
return 0;
}
}
return new MyComparable();
}
//方式二:匿名内部类的匿名对象
return new Comparable(){
@Override
public int compareTo(Object o){
return 0;
}
};
小注意点:在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话,要求此局部变量声明为final的。“复本”
jdk 7及之前的版本:要求此局部变量显式的声明为final的
jsk 8及之后的版本:可以省略final的声明
public class InnerclassTest{//外部类
public void method(){
int num=10;
class AA{//局部内部类
pulic void show(){
System.out.println(num);
}
}
}
}
应用场景:安卓开发
总结:
成员内部类和局部内部类,在编译以后,都会生成字节码文件。
格式:
成员内部类:外部类$内部类名.class
局部内部类:外部类$数字 内部类名.class
Comments NOTHING