- 1、static关键字
- 2、单例(Singleton)的设计模式
- 3、理解main()函数的语法(了解)
- 4、代码块
- 5、final 关键字
1、static 静态的
2、static 可以用来修饰:属性、方法、代码块、内部类,不能用来修饰构造器
3、使用static修饰属性:静态变量(类变量)
3.1、属性,按是否使用static修饰,分为:静态属性 和 非静态属性(实例变量)
实例变量: 当我们创建了多个对象,每个对象都独立的拥有一套类中的非静态属性,当修改其中一个对象中的非静态属性时,不会导致其他对象中的同样属性值的修改。(实例变量随着类的创建而加载,在实例化变量的时候才会被加载)
静态变量: 当我们创建了多个对象,多个对象共享同一个静态变量,当通过某个对象修改静态变量时,会导致其他对象调用此对象时是修改过的。(静态变量随着类的加载而加载)
可以理解为实例变量是房间里的东西,静态变量是客厅里的东西,房间里的东西是每个房间主人的,客厅里的东西是共有的。
3.2、static修饰属性的其他说明。
(1)、静态变量随着类的加载而加载。可以通过 类.变量名 的方式来调用。
(2)、静态变量的加载早于对象的创建。
(3)、由于类只会加载一次,则静态变量在内存也只存在一份,存在方法区的静态域中。
(4)、可否通过类,对象去调用实例变量?
| 类变量 | 实例变量 | |
|---|---|---|
| 类 | yes | no |
| 对象 | yes | yes |
3.3、静态属性举例,System.out,Math.PI。
public class StaticTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "XiaoMing";
c1.age = 40;
c1.nation = "CHN";//给c1的static nation赋值为CHN
Chinese c2 = new Chinese();
c2.name = "PanHu";
c2.age = 30;
//此时堆空间中有c1和c2两个对象,
//age每个对象各有一份,修改一个属性时,不会影响另一个对象属性值修改。
System.out.println(c2.nation);//注:nation只给了c1赋值,没有给c2赋值 输出结果CHN。
//给c2的赋值static nation赋值,再输出c1的nation
c2.nation = "CHINA";
System.out.println(c1.nation);//输出结果CHINA
}
}
//中国人
class Chinese{
String name;
int age;
static String nation;
}
内存结构如下:
栈:主要存局部变量
堆:new出来的结构(对象、数组)
方法区:类的加载信息、静态域、常量池
类变量与实例变量内存的解析:
4、使用static修饰的方法,静态方法
(1)、随着类的加载而加载
(2)、可否通过类或对象调用静态或非静态方法?
| 静态方法 | 非静态方法 | |
|---|---|---|
| 类 | yes | no |
| 对象 | yes | yes |
public class Static_test {
public static void main(String[] args) {
Static_test.run();//类名.方法名 调用
// Static_test s = new Static_test();
// s.eat();//需要实例化对象才能调用
}
public void eat(){
System.out.println("干饭!");
}
public static void run(){
System.out.println("跑的贼快!");
//eat();
}
}
(3)、在静态的方法中,只能调用静态的方法或属性,对于非静态方法,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。(主要原因是静态方法是类加载的时候就已经加载了,而非静态方法需要在类被实例化的时候才会被加载,生命周期没有静态变量长)
5、static注意点
(1)、在静态方法内,不能使用this关键字、super关键字。(静态结构前面省略的关键字不是 this. 或 super. ,省略的是 类名.)
(2)、关于静态属性和静态方法的使用,都可以从生命周期的角度去理解。
总结:
在开发中,如何确定一个属性是否要声明成static?
属性是可以被多个对象所共享的,不会随着对象的不同而不同的
类中的常量也常常声明为static
在开发中,如何确定一个方法是否要声明成static?
操作静态属性的方法,通常设置为静态的
工具类中的方法,习惯上声明的是static的,比如:Math、Arrays、Collection
例1:
//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自动增加
id=init++;//体现共享的概念,init的值不会变回1001
total++;
}
public Circle(double radius){
this();//调用无参的Circle
// id=init++;
// total++;
this.radius = radius;
}
private static int total=0; //基类圆的个数
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;
}
}
例2:
编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。账号要自动生成。
编写主类,使用银行账户类,输入、输出3个储户的上述信息。
考虑:哪些属性可以设计成static属性。
//账户类
public class Account {
private int id;
private String pwd = "000000";
private double balance;
private static double interestRate;
private static double minMoney = 1.0;
private static int init = 1001;//用于自动生成id
public Account(){
id = init++;//自动生成id
}
public Account(String pwd,double balance){//带参,设置账户
id = init++;
this.pwd = pwd;
this.balance = balance;
}
public int getId() {
return id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public double getBalance() {
return balance;
}
public static double getInterestRate() {
return interestRate;
}
public static void setInterestRate(double interestRate) {
Account.interestRate = interestRate;
}
public static double getMinMoney() {
return minMoney;
}
public static void setMinMoney(double minMoney) {
Account.minMoney = minMoney;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", pwd='" + pwd + ''' +
", balance=" + balance +
'}';
}
}
//测试类
public class AccountTest {
public static void main(String[] args) {
Account acct1 = new Account();
Account acct2 = new Account("123456",250);
System.out.println(acct1.toString());//Account{id=1001, pwd='000000', balance=0.0}
System.out.println(acct2.toString());//Account{id=1002, pwd='123456', balance=250.0}
System.out.println(acct1.getInterestRate());//0.0
System.out.println(acct1.getMinMoney());//1.0
}
}
2、单例(Singleton)的设计模式
设计模式: 是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模免去我们自己再思考和摸索。
就像是经典的棋谱,不同的棋局,我们用不同的棋谱。”套路”
单例设计模式: 就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
单例模式构造方式: 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
1、饿汉式
//1、单例设计模式就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
//2、如何实现
public class SingletonTest1 {
public static void main(String[] args) {
//Bank bank = new Bank();
Bank bank1 = Bank.getIntstance();
Bank bank2 = Bank.getIntstance();
System.out.println(bank1==bank2);//true
//bank1和bank2是一样的,创建出来的对象是唯一的一个,称为单例
}
}
//饿汉式
class Bank{
//私有化构造器:目的是避免在bank类外面去调用构造器,即不能在类的外面实例化
private Bank(){
}
//2、内部创建对象,要求此对象是静态的
private static Bank intstance = new Bank();
//3、提供公共的静态方法,返回类的对象
public static Bank getIntstance(){
return intstance;
}
}
2、懒汉式
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、声明当前类对象,没有初始化
private static Order instance = null;
//3、声明一个public static的返回当前类的对象的方法
public static Order getInstance(){
if(instance==null) {//判断是否实例化过对象
instance = new Order();
}
return instance;
}
}
懒汉式和饿汉式的区别在于是否一开始就实例化好对象
饿汉式:一开始就实例化
坏处:对象加载时间过长,与static关键字相同。
好处:饿汉式是线程安全的。
private static Bank intstance = new Bank();
懒汉式:不急着实例化
好处:延迟创建对象,不浪费内存空间。
目前写法坏处:线程不安全问题。
private static Order instance = null;
单例设计模式的好处
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。举例 java.lang.Runtime
应用场景
package Singleton_test;
public class MainTest {
public static void main(String[] args) {//入口
Main.main(new String[100]);
MainTest mainTest = new MainTest();
mainTest.show();
}
public void show(){
}
}
class Main{
public static void main(String[] args) {
args = new String[100];
for(int i=0;i
面试题:
此处,Something类的文件名叫OtherThing.java
class Something {
public static void main(String[] something_to_do) {
System.out.println("Do something ...");
}
}
上述程序是否可以正常编译、运行?
4、代码块
类的成员之四:代码块(初始化块)
1、代码块的作用:用来初始化类或者对象
2、代码块如果有修饰的话,只能使用static。
3、分类:静态代码块、非静态代码块。
4、静态代码块:
内部可以有输出语句
随着类的加载而执行,且只执行一次
作用:初始化类的信息
如果一个类中定义了多个静态代码块,那么按照声明的先后顺序执行
静态代码块的执行,优先于非静态代码块。
静态代码块内,只能调用静态的属性和静态的方法,不能调用非静态的结构
5、非静态代码块:
内部可以有输出语句
随着对象的创建而执行,且每创建一个对象,就执行一次
作用:可以在创建对象是,对对象的属性进行初始化。
(对属性可以赋值的位置:默认初始化、显示初始化、构造中初始化、有了对象以后,可以通过“对象,属性“或”对象,方法"的方式,进行赋值、在代码块中赋值)
如果一个类中定义了多个非静态代码块,那么按照声明的先后顺序执行
非静态的代码块内,可以调用静态的属性,静态的方法或非静态的属性和非静态的方法
非静态代码块的执行顺序小于构造函数
构造器与静态代码块的先后是静态代码块优先
public class BlockTest {
public static void main(String[] args) {
Person.desc = Person.desc;
System.out.println(Person.desc);//My name is XiaoMing
Person p1 = new Person();
Person p2 = new Person();
}
}
class Person{
//属性
String name;
int age;
static String desc = "我是一个人";
//构造器
public Person(){
System.out.println("123");
}
public Person(String name,int age){
this.age = age;
this.name = name;
}
//代码块
static {
System.out.println("hello,static block-1");
desc = "My name is XiaoMing";
}
static {
System.out.println("hello,static block-2");
desc = "My name is XiaoMing";
run();//调用静态方法
//eat()//不能调用非静态的方法
}
{
System.out.println("hello,block");
run();//调用静态方法
eat();//调用非静态方法
}
//方法
public void eat(){
System.out.println("干饭!");
}
public static void run(){
System.out.println("跑的贼快!");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
hello,static block-1
hello,static block-2
跑的贼快!
My name is XiaoMing
hello,block
跑的贼快!
干饭!
123
hello,block
跑的贼快!
干饭
例1:
//总结:由父及子,静态先行
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的构造器
例2:
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();
}
}
11111111111
44444444444
77777777777
22222222222
33333333333
55555555555
66666666666
22222222222
33333333333
55555555555
66666666666
22222222222
33333333333
//对属性可以赋值的位置:
// 1、默认初始化、
// 2、显示初始化、
// 3、构造中初始化、
// 4、有了对象以后,可以通过“对象,属性“或”对象,方法"的方式,进行赋值、
// 5、在代码块中赋值
//顺序:1--2/5--3--4
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
System.out.println(order.orderId);
}
}
class Order{
{
orderId = 4;
}
int orderId = 3;
}
5、final 关键字
final:最终的
1、final可以用来修饰的结构:类、方法、变量
2、final修饰一个类:此类不能被其他类所继承,如:String类、System类、StringBuffer类等
3、final修饰一个方法:表明此方法不能被重写。 如:Object类中的getClass();
4、final 修饰一个变量:此时的”变量“就称为是一个常量。
4.1、final修饰一个属性,可以考虑的位置有:显示初始化,代码块中赋值、初始化、构造器中初始化
4.2、final修饰一个局部变量:尤其是使用final来修饰形参时,表示此形参是一个常量,我们调用此方法时,给常量赋一个实参,一旦赋值以后,就只能在此方法体内使用,但是不能修改。
static final :
用来修饰属性:全局常量。
用来修饰方法:该方法可直接通过类来调用,但这个方法,不能被重写
(1)final修饰一个类:此类不能被其他类所继承
2、final修饰一个方法:表明此方法不能被重写
3、修饰一个变量,这个变量不能被修改
4、final修饰一个局部变量
public class FinalTest {
public static void main(String[] args) {
FinalTest finalTest = new FinalTest();
finalTest.show(20);
}
public void show(){
final int NUM = 10;
// NUM+=20;
}
public void show(final int num){
System.out.println(num);//20
// num=30//传入的形参只能使用,不能修改
}
}
例1:
public class Something {
public int addOne(final int x) {
return ++x;//这个不行,x变化
// return x + 1;//这个可以,x没变
}
}
例2:
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++;//o是常量,但是i是变量
}
}
class Other {
public int i;
}



