单例设计模式
情形一:
package com.atguigu.java;
/*
* 单例设计模式----->饿汉式
* 一上来就造好对象的成为饿汉式
*/
public class Singleton {
public static void main(String[] args) {
//直接通过“类.方法”调用静态方法
Bank bank1 = Bank.getInstance();
Bank bank2=Bank.getInstance();
System.out.println(bank1==bank2); //true。两个变量为同一个变量,地址值相同,都是下面唯一new的那一个对象
}
}
class Bank{
//1,私有化类的构造器:为了避免在Bank类外面调用构造器
private Bank(){
}
//2,内部创建类的对象(要求此对象必须是static)
private static Bank instance=new Bank();
//3,提供公共的静态的(需要是static)方法,返回类的对象。
public static Bank getInstance(){
return instance; //静态的方法中只能调用静态的结构
}
}
情形二:
package com.atguigu.java;
/*
*单例设计模式---->懒汉式
*啥时候用啥时候造为懒汉式
*/
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,声明当前类对象(此对象必须为static的),注意此处没有初始化----即啥时候用啥时候造为懒汉式
private static Order instance=null;
//3,声明一个public、static的返回当前类对象的方法
public static Order getInstance(){ //返回值为Order类型
if(instance==null){ //null说明还没有创建过对象,则需要做实例化,然后return
instance =new Order();
}
return instance; //不是null时,进行return
}
}
单例模式应用场景:
类的成员之代码块
代码理解:
package com.atguigu.java1;
/*
* 类的成员之代码块(或初始化块)
* 1,代码块的作用;用来初始化类、对象;
* 2,代码块如果有修饰的话,只能使用static;因此代码块分为静态代码块和非静态代码块
* 3,静态代码块:(作用:初始化类的信息)
* ①内部可以用输出语句
* ②随着类的加载而**执行**,而且只执行一次
* ③如果一个类中地点定义了多个静态代码块,则按照声明的先后顺序执行
* ④静态代码块只能调用静态的属性、静态的方法,不能调用非静态的结构
*
* 非静态代码块:(作用:非静态代码块可以在创建对象时,对对象的属性等进行初始化)
* ①内部可以用输出语句
* ②随着对象的创建而**执行**,而且是每创建一个对象,就执行一次非静态代码块;
* ③如果一个类中地点定义了多个非静态代码块,则按照声明的先后顺序执行
* ④非静态代码块可以调用静态的属性、静态的方法、或非静态的属性、非静态的方法
*注意静态代码块(随类的加载而加载-)的执行要先于非静态代码块的执行。
*/
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);
}
}
class Person{
//属性
String name;
int age;
static String desc="我是个好人";
//构造器
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//方法
public void eat(){
System.out.println("干饭");
}
//重写toString
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public static void info(){ //静态的
System.out.println("我是一个快乐的人!");
}
//代码块
//非静态代码块
{
System.out.println("hello,block-1");
//调用非静态结构
age=1; //初始化
eat();
//调用静态结构
desc="我是一个爱学习的人";
info();
}
{
System.out.println("hello,block-2");
}
//静态代码块
static{
System.out.println("hello,static block--2");
}
static{
System.out.println("hello,static block--1");
desc="我是一个爱学习的人"; //对类中静态属性进行了重新赋值
info(); //调用静态结构
//不可调用非静态结构
// eat();
// name="Tom";
}
}
体会一下结构复杂时执行的先后顺序问题:
package com.atguigu.java1;
/*由父及子,静态先行*/
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
//注意此处省略掉的super();结构---->调用父类Object空参构造器---->所以先把Object类结构加载过来再加载Root类......
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
//注意此处省略掉的super();结构---->调用父类Root空参构造器
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 com.atguigu.java1;
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();
}
}
回顾属性可以赋值的位置:
- ①默认初始化
- ②显式初始化
- ③构造器初始化
- ④有了对象之后,可以通过“对象.属性”、“对象.方法”的方式,进行赋值
- ⑤在代码块中赋值
顺序:①–②--⑤–③--④
注意:
1,显式初始化和代码块中赋值,谁顺序在前谁先执行。一般写代码先显式初始化。
2,代码块执行要先于构造器
final关键字
package com.atguigu.java1;
/*
* final:最终的
* 1.final可以修饰的结构:类、方法、变量
* 2.final用来修饰一个类:表明此类就不能被其他类继承,不能再扩充功能了(final修饰它就没孩子了)
* 如String类、System类、StringBuffer类都声明为final,都不能被继承,也代表不需要再扩充功能了
* 3.final用来修饰一个方法:表明此方法不可以再被重写
* 如Object类中的getClass()方法(获取当前对象所属的类)就是用final修饰
* 4.final修饰变量:此时的“变量”就称为一个常量
* 4.1.final修饰属性可以考虑赋值的位置为:显式初始化、代码块中初始化、构造器中初始化
* 4.2.final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当调用此方法时给常量形参赋一个实参。一旦赋值就只能在方法体内使用此形参,但不能修改
*
*/
public class FinalTest {
final int WIDTH=10; //显式初始化可行。既然变为常量我们一般大写
final int LEFT; //代码块中初始化可行
{
LEFT=1;
}
final int RIGHT;
public FinalTest(){
RIGHT=2; //构造器中初始化可行。
}
public FinalTest(int n){ //另一个构造器,也需要把上面构造器中的操作加入才正确
RIGHT=2;
}
public void show(){ //局部变量
final int NUM=10; //final修饰变为常量
// NUM+=20; 报红。常量,不可再赋值
}
public void show(final int num){ //形参也是局部变量
// num=20; final修饰形参后只能调用,不可修改
System.out.println(num);
}
public void doWidth(){
// width=20; 报红,此处不可修改
}
public static void main(String[] args) {
int num=10;
num=num+5;
FinalTest test=new FinalTest();
test.show(10); //调 的时候给形参赋值
}
}
final class FinalA{
}
//class c extends FinalA{} 注释打开此处会报红
class AA{
public final void show(){ //此处为final
}
}
class BB extends AA{
//此处相当于重写
// public void show(){ 注释打开会报红,final方法不可以被重写
}
final练习: