目录
- 01、static
- 1.1、static的使用
- 1.2、static应用举例
- 1.3、单例(Singleton)设计模式
- 02、main方法的语法
- 03、类的成员之四:代码块
- 04、关键字:final
- 05、抽象类与抽象方法
- 5.1、多态的应用:模板方法设计模式(TemplateMethod)
- 06、接口(interface)
- 6.1、概述
- 6.2、接口的应用:代理模式(Proxy)
- 07、Java 8 中关于接口的改进
01、static
我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份。
1.1、static的使用
static 关键字的使用
1.static:静态的。
2.static 可以用来修饰:属性、方法、代码块、内部类。
3.使用 static 修饰属性:静态变量(或类变量)。
3.1 属性:是否使用 static 修饰,又分为:静态属性 VS 非静态属性(实例变量)
实例变量:我们创建了类的多个对象,每个对象都独立的拥有了一套类中的非静态属性。当修改其中一个非静态属性时,不会导致其他对象中同样的属性值的修饰。
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过静态变量去修改某一个变量时,会导致其他对象调用此静态变量时,是修改过的
。
3.2 static 修饰属性的其他说明:
① 静态变量随着类的加载而加载
。可以通过"类.静态变量"的方式进行调用
。
② 静态变量的加载要早于对象的创建。
③ 由于类只会加载一次,则静态变量在内存中也只会存在一次
。存在方法区的静态域中。
④ 类变量 实例变量
类 yes no
对象 yes yes
3.3 静态属性举例:System.out ; Math.PI;
4.使用 static 修饰方法:静态方法
① 随着类的加载而加载,可以通过"类.静态方法"
的方式调用
② 方法 非静态方法
类 yes no
对象 yes yes
③静态方法中,只能调用静态的方法或属性
非静态的方法中,可以调用所有的方法或属性
5.static 注意点:
5.1在静态的方法内,不能使用 this 关键字、super 关键字
5.2 关于静态属性和静态方法的使用,大家从生命周期的角度去理解。
6.开发中,如何确定一个属性是否需要声明 static 的?
》 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
》 类中的常量也常常声明为 static
开发中,如何确定一个方法是否要声明为 static 的?
》 操作静态属性的方法,通常设置为 static 的
》 工具类中的方法,习惯上声明为 static 的。比如:Math、Arrays、Collections
public class StaticTest {public static void main(String[] args) {Chinese.nation = "中国";Chinese c1 = new Chinese();c1.name = "姚明";c1.age = 40;c1.nation = "CHN";Chinese c2 = new Chinese();c2.name = "马龙";c2.age = 30;c2.nation = "CHINA";System.out.println(c1.nation); //编译不通过
// Chinese.name = "张继科";}
}
//中国人
class Chinese{String name;int age;static String nation;
}
static修饰方法
public class StaticTest {public static void main(String[] args) {Chinese.nation = "中国";Chinese c1 = new Chinese();//编译不通过
// Chinese.name = "张继科";c1.eat();Chinese.show();//编译不通过
// chinese.eat();
// Chinese.info();}
}
//中国人
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 = "Tom";//可以调用静态的结构System.out.println(Chinese.nation);walk();}public void info(){System.out.println("name : " + name + ",age : " + age);}public static void walk(){}
}
1.2、static应用举例
//static 关键字的应用
public class CircleTest {public static void main(String[] args) {Circle c1 = new Circle();Circle c2 = new Circle();Circle c3 = new Circle();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; //需要自动赋值public Circle(){id = init++;total++;}public Circle(double radius){this();//或
// id = init++;
// total++;this.radius = radius;}private static int total;//记录创建圆的个数private static int init = 1001;//static 声明的属性被所有对象所共享public double findArea(){return 3.14 * 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;}}
1.3、单例(Singleton)设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。”套路”
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为 private
,这样,就不能用 new 操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
1、单例模式的饿汉式
/** 单例设计模式:* 1.所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例* * 2.如何实现?* 饿汉式 VS 懒汉式* * 3.区分饿汉式和懒汉式。* 饿汉式:坏处:对象加载时间过长。* 好处:饿汉式是线程安全的。* * 懒汉式:好处:延迟对象的创建。* 坏处:目前的写法,会线程不安全。---》到多线程内容时,再修改*/
public class SingletonTest {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.要求此对象也必须声明为静态的private static Bank instance = new Bank();//3.提供公共的静态的方法,返回类的对象。public static Bank getInstance(){return instance;}
}
2、单例模式的懒汉式
/** 单例的懒汉式实现* */
public class SingletonTest2 {public static void main(String[] args) {Order order1 = Order.getInstance();Order order2 = Order.getInstance();System.out.println(order1 == order2);}
}
class Order{//1.私有化类的构造器private Order(){}//2.声明当前类对象,没有初始化。//此对象也必须声明为 static 的private static Order instance = null;//3.声明 public、static 的返回当前类对象的方法public static Order getInstance(){if(instance == null){instance = new Order(); }return instance;}
}
3、单例模式的优点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
4、单例(Singleton)设计模式-应用场景
- 网站的计数器,一般也是单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
- Application也是单例的典型应用
- Windows 的Task Manager (任务管理器)就是很典型的单例模式
- Windows 的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
02、main方法的语法
由于 Java 虚拟机需要调用类的 main()方法,所以该方法的访问权限必须是 public,又因为 Java 虚拟机在执行 main()方法时不必创建对象,所以该方法必须是 static 的,该方法接收一个 String 类型的数组参数,该数组中保存执行 Java 命令时传递给所运行的类的参数。
又因为 main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,
这种情况,我们在之前的例子中多次碰到。
/** main()方法的使用说明* 1.main()方法作为程序的入口;* 2.main()方法也是一个普通的静态方法* 3.main()方法也可以作为我们与控制台交互的方式。(之前,使用 Scanner)* * */
public class MainTest {public static void main(String[] args) { //入口Main.main(new String[100]);MainTest test = new MainTest();test.show();}public void show(){}
}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]);}}
}
03、类的成员之四:代码块
代码块(或初始化块)
1.代码块的作用:用来初始化类、对象的
2.代码块如果有修饰
的话,只能使用 static
3.分类:静态代码块 vs 非静态代码块
4.静态代码块
- 内部可以有输出语句
随着类的加载而执行,而且只执行一次
- 作用:初始化类的信息
- 如果一个类中,定义了多个静态代码块,则按照声明的先后顺序执行
- 静态代码块的执行,优先于非静态代码块的执行
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
5.非静态代码块
- 内部可以有输出语句
随着对象的创建而执行
每创建一个对象,就执行一次非静态代码块。
- 作用:可以在创建对象时,对对象的属性等进行初始化。
- 如果一个类中,定义了多个非静态代码块,则按照声明的先后顺序执行
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法。
对属性可以赋值的位置:
①默认初始化
②显式初始化
③构造器中初始化
④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值。
⑤在代码块中赋值
public class BlockTest {public static void main(String[] args) {String desc = Person.desc;System.out.println(desc);Person p1 = new Person();Person p2 = new Person();System.out.println(p1.age);Person.info();}
}class Person{//属性String name;int age;static String desc = "我是一个青年";//构造器public Person(){}//static 的代码块static{System.out.println("hello,static block-1");//调用静态结构desc = "我是一个爱小说的人";info();//不能调用非静态结构
// eat();
// name = "Tom";}static{System.out.println("hello,static block-2");}//非 static 的代码块{System.out.println("hello,block-2");}{System.out.println("hello,block-1");//调用非静态结构age = 1;eat();//调用静态结构desc = "我是一个爱小说的人 1";info();} //方法public Person(String name,int age){this.name = name;this.age = age;}public void eat(){System.out.println("吃饭");}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}public static void info(){System.out.println("我是一个快乐的人。");}}
静态初始化块举例
总结:由父类到子类,静态先行
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();}
}
04、关键字:final
1.final可以用来修饰的结构:类、方法、变量
2.final用来修饰一个类:此类不能被其他类所继承
。比如:String类、System类、StringBuffer类
3.final修饰一个方法:final标记的方法不能被子类重写
。比如:Object类中的getClass()。
4.final用来修饰变量:此时的"变量"(成员变量或局部变量)就是一个常量。名称大写,且只能被赋值一次。
4.1 final修饰属性,可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
4.2 final修饰局部变量:
尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。
一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰:全局常量
public class FinalTest {final int WIDTH = 0;final int LEFT;final int RIGHT;
// final int DOWN;{LEFT = 1;}public FinalTest(){RIGHT = 2;}public FinalTest(int n){RIGHT = n;}// public void setDown(int down){
// this.DOWN = down;
// }public void dowidth(){
// width = 20; //width cannot be resolved to a variable}public void show(){final int NUM = 10; //常量
// num += 20;}public void show(final int num){System.out.println(num);}public static void main(String[] args) {int num = 10;num = num + 5;FinalTest test = new FinalTest();
// test.setDown(5);test.show(10);}
}final class FianlA{}//class B extends FinalA{ //错误,不能被继承。
//
//}//class C extends String{
//
//}class AA{public final void show(){}
}//class BB extends AA{ // 错误,不能被重写。
// public void show(){
//
// }
//}
05、抽象类与抽象方法
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类
1.abstract:抽象的
2.abstract 可以用来修饰的结构:类、方法
3.abstract修饰类:抽象类
- 此类
不能实例化
- 抽象类中
一定有构造器
,便于子类实例化时调用(涉及:子类对象实例化全过程)- 开发中,都会提供抽象类的子类,让子类对象实例化,实现相关的操作
4.abstract
修饰方法:抽象方法
- 抽象方法,
只有方法的声明,没有方法体。
- 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法
- 若子类重写了父类中所有的抽象方法,此子类方可实例化;若子类吗,没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
abstract 使用上的注意点:
1.abstract不能用来修饰变量、代码块、构造器
;
2.abstract 不能用来修饰私有方法、静态方法、final 的方法、final 的类。
public class AbstractTest {public static void main(String[] args) {//一旦 Person 类抽象了,就不可实例化
// Person p1 = new Person();
// p1.eat();}
}abstract class Creature{public abstract void breath();
}abstract class Person extends Creature{String name;int age;public Person(){}public Person(String name,int age){this.name = name;this.age = age;}//不是抽象方法
// public void eat(){
// System.out.println("人吃饭");
// }//抽象方法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(){System.out.println("学生应该多吃有营养的。");}@Overridepublic void breath() {System.out.println("学生应该呼吸新鲜的无雾霾空气");}
}
抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类
5.1、多态的应用:模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式
。
06、接口(interface)
6.1、概述
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java 不支持多重继承。有了接口,就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有 is-a 的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3 机、手机、数码相机、移动硬盘等都支持 USB 连接。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是"能不能"的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
接口(interface)是
抽象方法和常量值定义的集合
。
接口的特点:
- 用 interface 来定义。
- 接口中的所有
成员变量都默认是由 public static final 修饰的
。- 接口中的所有
抽象方法都默认是由 public abstract 修饰的
。- 接口中
没有构造器
。- 接口采用多继承机制。
1.接口使用 interface 来定义。
2.在 Java 中:接口和类是并列的两个结构
3.如何去定义两个接口:定义接口中的成员
3.1 JDK7 及以前:只能定义全局常量和抽象方法
全局常量:public static final 的,但是书写中,可以省略不写。
抽象方法:public abstract 的
3.2 JDK8:除了全局常量和抽象方法之外,还可以定义静态方法、默认方法。
4.接口中不能定义构造器!意味着接口不可以实例化。
5.Java 开发中,接口通过让类去实现(implements)的方式来使用。如果实现类覆盖了接口中的所有方法,则此实现类就可以实例化;如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
6.Java 类可以实现多个接口 -->弥补了 Java 单继承性的局限性
格式:class AA extends BB implementd CC,DD,EE
7.接口与接口之间是继承,而且可以多继承
8.接口的具体使用,体现多态性
接口的主要用途就是被实现类实现。(面向接口编程)
public class InterfaceTest {public static void main(String[] args) {System.out.println(Flayable.MAX_SPEED);System.out.println(Flayable.MIN_SPEED);}
}
interface Flayable{//全局变量public static final int MAX_SPEED = 7900; int MIN_SPEED = 1;//省略了 public static final //抽象方法public abstract void fly();void stop();//省略了 public abstract //Interfaces cannot have constructors
// public Flayable(){
//
// }
}
interface Attackable{void attack();
}class Plane implements Flayable{@Overridepublic void fly() {System.out.println("飞机通过引擎起飞");}@Overridepublic void stop() {System.out.println("驾驶员减速停止");}}
abstract class Kite implements Flayable{@Overridepublic void fly() {}
}class Bullet extends Object implements Flayable,Attackable,CC{@Overridepublic void attack() {// TODO Auto-generated method stub}@Overridepublic void fly() {// TODO Auto-generated method stub}@Overridepublic void stop() {// TODO Auto-generated method stub}@Overridepublic void method1() {// TODO Auto-generated method stub}@Overridepublic void method2() {// TODO Auto-generated method stub}
}//*********************************
interface AA{void method1();
}
interface BB{void method2();
}
interface CC extends AA,BB{}
举例
/** 接口的使用* 1.接口使用上也满足多态性* 2.接口,实际上就是定义了一种规范* 3.开发中,体会面向接口编程!* */
public class USBTest {public static void main(String[] args) {Computer com = new Computer();//1.创建了接口的非匿名实现类的非匿名对象Flash flash = new Flash();com.transferData(flash); //2. 创建了接口的非匿名实现类的匿名对象com.transferData(new Printer());//3. 创建了接口的匿名实现类的非匿名对象USB phone = new USB(){@Overridepublic void start() {System.out.println("手机开始工作");}@Overridepublic void stop() {System.out.println("手机结束工作");}};com.transferData(phone);//4. 创建了接口的匿名实现类的匿名对象com.transferData(new USB(){@Overridepublic void start() {System.out.println("mp3 开始工作");}@Overridepublic 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{@Overridepublic void start() {System.out.println("U 盘开始工作");}@Overridepublic void stop() {System.out.println("U 盘结束工作");}
}
class Printer implements USB{@Overridepublic void start() {System.out.println("打印机开启工作");}@Overridepublic void stop() {System.out.println("打印机结束工作");}}
6.2、接口的应用:代理模式(Proxy)
代理模式是 Java 开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
/** 接口的应用:代理模式* * */
public class NetWorkTest {public static void main(String[] args) {Server server = new Server();
// server.browse();ProxyServer proxyServer = new ProxyServer(server);proxyServer.browse();}
}
interface NetWork{public void browse();}
//被代理类
class Server implements NetWork{@Overridepublic 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("联网前的检查工作");}@Overridepublic void browse() {check();work.browse();}}
应用场景:
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有 100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用 proxy 来进行大图片的打开。
分类
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
JDK 自带的动态代理,需要反射等知识
public class StaticProxyTest {public static void main(String[] args) {Proxy s = new Proxy(new RealStar());s.confer();s.signContract();s.bookTicket();s.sing();s.collectMoney();}
}interface Star {void confer();// 面谈void signContract();// 签合同void bookTicket();// 订票void sing();// 唱歌void collectMoney();// 收钱
}
//被代理类
class RealStar implements Star {public void confer() {}public void signContract() {}public void bookTicket() {}public void sing() {System.out.println("明星:歌唱~~~");}public void collectMoney() {}
}//代理类
class Proxy implements Star {private Star real;public Proxy(Star real) {this.real = real;}public void confer() {System.out.println("经纪人面谈");}public void signContract() {System.out.println("经纪人签合同");}public void bookTicket() {System.out.println("经纪人订票");}public void sing() {real.sing();}public void collectMoney() {System.out.println("经纪人收钱");}
}
07、Java 8 中关于接口的改进
Java 8 中,你可以为接口添加静态方法和默认方法
。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:
使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体
。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像 Collection/Collections 或者 Path/Paths 这样成对的接口和类。
默认方法:
默认方法使用 default 关键字修饰。可以通过实现类对象来调用
。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API 中对 Collection、List、Comparator 等接口提供了丰富的默认方法。
/** JDK8:除了全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)。* * */
public interface CompareA {//静态方法public static void method1() {System.out.println("CompareA:西安");}//默认方法public default void method2(){System.out.println("CompareA:深圳");}default void method3(){System.out.println("CompareA:杭州");}
}