1. static关键字的引入

  2. 静态变量对比实例变量

  3. static修饰属性的其他说明

  4. 类变量和实例变量的内存解析

  5. static修饰方法

  6. 属性或方法是否应该加上static

  7. 自定义ArrayUnit工具类的优化

  8. static应用举例

  9. static练习

  10. 设计模式的介绍与单例模式

  11. 理解main()方法的语法

  12. 代码块

  13. 属性赋值先后顺序的完整过程

  14. final关键字

 

1,static关键字的引入

    当编写一个类时,就是在描述其对象具有的属性和行为。这时并没有产生对象,使用new关键字时,系统会为对象分配内存空间,它的属性和方法才可以被调用,且每个对象的内存中都有其单独的属性
    在一些情况下定义类时,希望无论通过这个类创建了多少个对象,某些特定数据都只有一份,比如定义中国人这个类,就希望国籍这一属性只有一份,没有必要在每个中国人对象中都加入这个属性

 

2,静态变量对比实例变量

&3,static修饰属性的其他说明

package com.atguigu.java;

/*
 * static可以用来修饰:属性、方法、代码块、内部类,不能用来修饰构造器
 * 修饰属性:
 *     被static修饰的属性称为静态变量或静态属性,没有被static修饰的属性称为实例变量或非静态属性
 *     当创建了类的多个对象时,每个对象都独立的拥有一套类中的非静态属性,但修改某个对象
 *         的非静态属性时,不会影响其他对象中的属性值;而对于静态属性,所有对象共享一个,
 *         当通过某个对象修改它后,其他对象再调用时就发生了改变
 *     静态变量也叫作类变量(因为是归类所有的),随着类的加载而被加载;也就是说静态变量的加载要
 *         早于对象的创建,早于实例变量
 *         由于类只会被加载一次,所以静态变量在内存中只有一份,存放在方法区的静态域中
 *         通过对象可以调用类变量和实例变量,因为对象的创建晚于类变量;类可以调用类变量但
 *             不能调用实例变量,一是因为后者晚于类的加载,二是如果能调用,那调用的是哪个对象
 *             的实例变量?
 * 举例:System.out、Math.PI都是通过类直接调用的静态结构
 */

public class StaticTest {
	
	public static void main(String[] args) {
		Chinese.nation = "中国"; // 类已经被加载过,可以直接通过类名调用静态变量
		
		Chinese c1 = new Chinese();
		c1.name = "姚明";
		c1.age = 40;
		
		Chinese c2 = new Chinese();
		c2.name = "马龙";
		c2.age = 30;
		
		c1.nation = "CHN";
		System.out.println(c2.nation); // CHN
		
		// 编译不通过
		// Chinese.name = "sfj"; // 类不能调用实例变量
	}
}

class Chinese {
	String name;
	int age;
	static String nation; // 所有对象共用一个
}

 

4,类变量和实例变量的内存解析

    类变量存放在方法区,在类被加载时就被创建,且只有一份

day14-面向对象(下)_面向对象章节

 

5,static修饰方法

package com.atguigu.java;

/*
 * static修饰的称为静态方法,该方法随着类的加载而加载。被static修饰的代码块、内部类
 *     都是如此,随着类的加载而加载
 *     实例变量和非静态方法都是随着对象的创建而被加载的。静态变量和静态方法属于类,而
 *         实例变量和非静态方法属于对象
 *     对象可以调用静态方法也可以调用非静态方法,类可以调用静态方法但不能调用非静态方法。
 *         因为要是非静态方法中调用了实例变量,通过类调用非静态方法时,应该调用的是哪个对象
 *         的非静态方法?若是没有创建对象,非静态方法根本就没被加载,当然就不能被调用
 *
 * 静态方法中只能调用静态方法或静态属性,非静态方法中既能调用非静态属性或方法,也能调用静态
 *     属性或方法,这是因为它们各自的生命周期
 * 使用static的注意点:
 *     在静态方法中,不能使用this或super关键字,这两个都是要基于有当前对象的前提下才能使用
 *     ,而没有对象时静态方法就能被直接调用
 */

public class StaticTest1 {
	
	public static void main(String[] args) {
		Chinese1.show();
		Chinese1 c1 = new Chinese1();
		c1.food = "米饭";
		Chinese1 c2 = new Chinese1();
		c2.food = "米粉";
		c1.eat();
		c2.eat();
		c1.show();
		// 不能通过类调用非静态方法
		// Chinese1.eat();
	}
	
}

class Chinese1 {
	String name;
	int age;
	String food;
	static String nation;

	public void eat() { // 只能通过对象调用
		System.out.println("吃" + food);
	}
	
	public static void show() { // 可以通过对象调用,也可以通过类直接调用
		System.out.println("我是一个中国人");
		System.out.println(nation); // 可以调用静态结构。省略了Chinese.nation
		// name = "Tom"; // this.name;
		// eat(); // this.eat(); // 不能在静态方法中调用非静态结构
	}
}
    总结静态结构和非静态结构的生命周期:

day14-面向对象(下)_面向对象章节_02

    以被加载的先后顺序来看,后者(对象)可以调用前者(类)的结构,但前者不能调用后者的结构

 

6,属性或方法是否应该加上static

    某个属性需要是对象特有的,就不要加上static;某些属性可以是共用一个的,当修改时就是要修改所有对象的该属性,就给属性加上static。常量通常声明为static,如Math.PI
    操作静态属性的方法,通常声明为static,就可以直接通过类来设置。因为如果还用对象调用非静态方法来设置静态属性,显得多此一举。工具类中的方法声明为static,比如Math、Arrays

 

7,自定义ArrayUnit工具类的优化

package com.atguigu.java1;

/*
 * 自定义数组的工具类
 * 
 */
public class ArrayUtil {

	// 求数组的最大值
	public static int getMax(int[] arr) {
		int maxValue = arr[0];
		for (int i = 1; i < arr.length; i++) {
			if (maxValue < arr[i]) {
				maxValue = arr[i];
			}
		}
		return maxValue;
	}

	// 求数组的最小值
	public static int getMin(int[] arr) {
		int minValue = arr[0];
		for (int i = 1; i < arr.length; i++) {
			if (minValue > arr[i]) {
				minValue = arr[i];
			}
		}
		return minValue;
	}

	// 求数组的总和
	public static int getSum(int[] arr) {

		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		return sum;
	}

	// 求数组的平均值
	public static int getAvg(int[] arr) {

		return getSum(arr) / arr.length;
	}

	//如下的两个同名方法构成了重载
	// 反转数组
	public static void reverse(int[] arr) {
		for (int i = 0; i < arr.length / 2; i++) {
			int temp = arr[i];
			arr[i] = arr[arr.length - i - 1];
			arr[arr.length - i - 1] = temp;
		}
	}
	
//	public static void reverse(String[] arr){
//		
//	}

	// 复制数组
	public static int[] copy(int[] arr) {
		int[] arr1 = new int[arr.length];
		for (int i = 0; i < arr1.length; i++) {
			arr1[i] = arr[i];
		}
		return arr1;
	}

	// 数组排序
	public static void sort(int[] arr) {
		// 冒泡排序
		for (int i = 0; i < arr.length - 1; i++) {

			for (int j = 0; j < arr.length - 1 - i; j++) {

				if (arr[j] > arr[j + 1]) {
//					int temp = arr[j];
//					arr[j] = arr[j + 1];
//					arr[j + 1] = temp;
					//错误的:
//					swap(arr[j],arr[j + 1]);
					//正确的:
					swap(arr,j,j + 1);
				}

			}

		}
	}
	
	//错误的:交换数组中指定两个位置元素的值
//	public void swap(int i,int j){
//		int temp = i;
//		i = j;
//		j = temp;
//	}
	//正确的:交换数组中指定两个位置元素的值
	private static void swap(int[] arr,int i,int j){
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	

	// 遍历数组
	public static void print(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + "\t");
		}
		System.out.println();
	}

	// 查找指定元素
	public static int getIndex(int[] arr, int dest) {
		// 线性查找:

		for (int i = 0; i < arr.length; i++) {

			if (dest == arr[i]) {
				return i;
			}

		}

		return -1;//返回一个负数,表示没有找到
	}

}
    测试

package com.atguigu.java1;

public class ArrayUtilTest {
	public static void main(String[] args) {
		
//		ArrayUtil util = new ArrayUtil();
		int[] arr = new int[]{32,34,32,5,3,54,654,-98,0,-53,5};
		int max = ArrayUtil.getMax(arr);
		System.out.println("最大值为:" + max);
		
		System.out.println("排序前:");
		ArrayUtil.print(arr);
		
		
		ArrayUtil.sort(arr);
		System.out.println("排序后:");
		ArrayUtil.print(arr);
		
//		System.out.println("查找:");
//		int index = util.getIndex(arr, -5);
//		if(index >= 0){
//			System.out.println("找到了,索引地址为:" + index);
//		}else{
//			System.out.println("未找到");
//		}
		
		
//		util.reverse(arr);
	}
}

 

8,static应用举例

package com.atguigu.java;

public class CircleTest {
	public static void main(String[] args) {
		Circle c1 = new Circle();
		Circle c2 = new Circle();
		
		System.out.println("c1的id:" + c1.getId());
		System.out.println("c2的id:" + c2.getId());
		
		System.out.println("创建的圆的个数为:" + Circle.getTotal());
	}
}

class Circle {
	private double radius;
	private int id;
	
	public Circle() {
		id = init++;
		total++;
	}
	public Circle(double radius) {
		this();
		this.radius = radius;
	}
	
	private static int total;
	private static int init = 1001;
	
	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() { // 调用静态属性的方法一般也声明为static,它们生命
								// 周期一样,不必通过专门创建对象来调用
		return total;
	}
	
}

 

9,static练习

package com.atguigu.java;

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;
	
	public Account() {
		id = init++;
	}
	
	public Account(String pwd, double balance) {
		id = init++;
		this.pwd = pwd;
		this.balance = balance;
	}
	
	public String getPwd() {
		return pwd;
	}
	public void setPwd(String pwd) {
		this.pwd = pwd;
	}
	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;
	}
	public int getId() {
		return id;
	}
	public double getBalance() {
		return balance;
	}

	public String toString() {
		return "Account [id=" + id + ", pwd=" + pwd + ", balance=" + balance + "]";
	}
	
}
    测试

package com.atguigu.java;

public class AccountTest {
	public static void main(String[] args) {
		Account acct1 = new Account();
		Account acct2 = new Account("qwerty", 2000);
		
		Account.setInterestRate(0.012);
		Account.setMinMoney(100);
		
		System.out.println(acct1);
		System.out.println(acct2);
		
		System.out.println(acct1.getInterestRate());
		System.out.println(acct1.getMinMoney());
	}
}

 

10,设计模式的介绍与单例模式

day14-面向对象(下)_面向对象章节_03

    单例模式的饿汉式实现

package com.atguigu.java;

public class SingletonTest {
	
	public static void main(String[] args) {
		Bank bank1 = Bank.getInstance(); // Bank类被加载时创建了它本身的一个对象
		Bank bank2 = Bank.getInstance(); // 且只有这一个对象。相当于有三个引用指向了这一个对象
		// 不管通过getInstance()返回多少个引用,它们都指向都一个对象
		System.out.println(bank1 == bank2); // true
	}
}

// 饿汉式单例:不管需不需要这个对象,都先创建好
class Bank {
	// 1,私有化类的构造器,避免在类的外部调用构造器,从而就不能new对象
	private Bank() {
		
	}
	// 2,在内部创建类的对象。对象中的该属性存储了对象本身
	// 4,静态方法只能调用静态属性,所以这里也要声明为static
	private static Bank instance = new Bank();
	// 3,提供公共方法,返回类的对象,将该方法声明为static
	// 如果不声明为static,则必须通过对象来调用,而又只能通过该方法返回对象
	public static Bank getInstance() {
		return instance;
	}
}
    单例模式的懒汉式实现

package com.atguigu.java;

public class SingletonTest1 {
	
	public static void main(String[] args) {
		Order order1 = Order.getInstance();
		Order order2 = Order.getInstance(); // 无论调用多少次getInstance()方法,new对象的行为只有一次
		
		System.out.println(order1 == order2); // true
	}
}

// 懒汉式单例:需要创建对象时才创建
class Order {
	// 1,私有化类的构造器
	private Order() {
		
	}
	// 2,声明当前类的对象的引用变量
	// 4,这里也要声明为static
	private static Order instance = null;
	// 3,返回类的对象,声明为static
	public static Order getInstance() {
		if(instance == null) { // 如果没有创建过对象,就创建一个对象。且之后都只返回同一个对象的引用
			instance = new Order();
		}
		return instance;
	}
}
    懒汉式和饿汉式的对比
        饿汉式中对象一开始就被创建,如果没有被使用就会一直占用内存空间,这也是
        static属性的特点,直到类被销毁才会消失
        饿汉式是线程安全的,上面的懒汉式实现是线程不安全的
        要能写出单例模式,现在还没有实现线程安全的懒汉式,所以最好写饿汉式实现
    单例模式的优点:因为只创建一个实例,所以能减少系统开销,当创建某些对象需要
        较多资源时,就可以通过生成一个单例对象,让其永久驻留内存
    单例模式使用场景
        java.lang.Runtime就是饿汉式实现的单例模式

day14-面向对象(下)_面向对象章节_04

 

11,理解main()方法的语法

package com.atguigu.java;

/*
 * main()作为程序的入口
 * 同时main()也是一个普通的静态方法
 */

public class MainTest {
	// main()是被JVM调用的,它的权限要足够大。如果不将它声明为static会怎样?而对象又必须是在main()中创建的
	public static void main(String[] args) { // 执行时选择将这个main()作为程序入口
		Main.main(new String[100]);
		// show(); // main()是一个静态方法,不能在它里面直接调用类中的非静态结构,需要先
				// 创建对象,通过对象来调用
	}
	
	public void show() {
		
	}
}

class Main {
	
	public static void main(String[] args) { // 作为一个普通的静态方法
		for(int i = 0 ; i < args.length ; i++) {
			args[i] = "args_" + i;
			System.out.println(args[i]);
		}
	}
}
    给main()传参,Run as->Configurations,这里的MainDemo就是编译后的字节码文件

day14-面向对象(下)_面向对象章节_05

 

12,代码块

package com.atguigu.java1;

/*
 * 代码块或叫做初始化块
 * 作用:初始化类或对象
 * 如果要修饰代码块,只能用static。所以就分为静态代码块和非静态代码块
 * 静态代码块随着类的加载被执行,且只执行一次;非静态代码块随着对象的创建被执行,创建几个对象就执行几次
 * 在静态代码块中可以给静态属性进行初始化;在非静态代码块中可以给对象的属性进行初始化
 * 可以定义多个(实际使用也不会定义多个)静态或非静态代码块,静态代码块早于所有非静态代码块
 *     的执行,在同类之中先执行定义在前面的
 * 还是生命周期的问题,在类被加载而对象还未创建时,类中的静态结构已经被加载,而非静态结构是
 *     创建对象后才被加载的。所以静态代码块中可以调用静态结构,不能调用非静态结构;而非
 *     静态代码块可以调用静态和非静态结构
 */

public class BlockTest {
	
	public static void main(String[] args) {
		String desc = Person.desc; // 当使用一个类时,类就被加载,类中的静态结构也被加载。静态代码块被执行
		String desc1 = Person.desc; // 类没有被第二次加载,类中的静态结构没有被第二次加载。静态代码块没有被第二次执行
		Person p1 = new Person(); // 类中非静态代码块被执行
		Person p2 = new Person(); // 类中非静态代码块被执行
	}
}

class Person {
	String name;
	int age;
	static String desc = "我是一个人";
	
	public Person() {
		
	}
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	// 代码块没有名,所以不能直接调用它
	// 非静态代码块。代码块中可以写输出语句
	{
		age = 1; // 每创建一个对象,都将它的age属性设为1
		System.out.println("hello, block");
	}
	// 静态代码块
	static {
		desc = "我是一个中国人"; // 当类被加载时调用,对静态属性进行初始化
		System.out.println("hello, static block-1");
	}
	static {
		System.out.println("hello, static block-2");
	}
	
	public void eat() {
		System.out.println("吃饭");
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
}
    练习1

package com.atguigu.java1;

//总结:父先子后,静态先行

class Root {
	static {
		System.out.println("Root的静态初始化块");
	}
	{
		System.out.println("Root的普通初始化块");
	}
	public Root() {
		super();
		System.out.println("Root的无参数的构造器");
	}
}
class Mid extends Root {
	static {
		System.out.println("Mid的静态初始化块");
	}
	{
		System.out.println("Mid的普通初始化块");
	}
	public Mid() {
		super();
		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(); // 在创建一个对象之前必定加载好类,newLeaf()就是在调用构造器,
					// 然后通过super()一直调用到Object的构造器,执行后回到Root类
					// 中,此时并没有接着执行Root构造器的输出语句,因为输出语句执行完,
					// 就意味着Root的构造器执行完(对象创建完成),而静态代码块还没随着
					// 类的加载而加载,所以下一步执行所有类的静态代码块,表明所有类都
					// 被加载过一次;接着是从上到下的每个类,先执行非静态代码块(随着对象
					// 创建而被加载),再执行完构造器(对象创建完成)
		System.out.println();
		new Leaf(); // 所有类及其中的静态结构已经被加载过一次,就不会再加载。所以只执行
					// 非静态代码块和构造器
	}
}
    执行结果

day14-面向对象(下)_面向对象章节_06

    练习2

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"); // main()既是程序入口也是一个普通的静态
									// 方法,要调用它必定先加载它所在的Son类。由上一
							// 个程序可知,会先将它的所有父类及自己的静态代码块执行一次
							// ,表明已经将类加载过,然后执行这条和下一条输出语句
		System.out.println("************************");
		new Son(); // 所有类已被加载过一次,要创建对象时,先执行父类中的非静态代码块和
				// 构造器,再执行子类中的
		System.out.println("************************");
		new Son();
		System.out.println("************************");
		new Father();
	}
}
    执行结果

day14-面向对象(下)_面向对象章节_07

 

13,属性赋值先后顺序的完整过程

package com.atguigu.java1;

import com.atguigu.java.MainDemo;

/*
 * 属性可以在这些位置赋值,赋值顺序从前到后:
 *     ①默认初始化  ②显示初始化/在代码块中赋值  ③构造器中初始化  ④通过对象.属性或对象.方法赋值
 */

public class OrderTest {
	
	public static void main(String[] args) {
		Order order = new Order(5);
		System.out.println(order.orderId); 
	}
}

class Order {
	int orderId = 3; // 显示和代码块谁写在前就是谁先赋值
	{
		orderId = 4;
	}
	public Order(int orderId) {
		this.orderId = orderId;
	}
}

 

14,final关键字

package com.atguigu.java1;

/*
 * final可以用来修饰类、方法、变量
 * 修饰类时,该类不能被继承。比如String类、System类
 * 修饰方法时,此方法不能被重写。比如Object的getClass()
 * 修饰变量时,此“变量”变为一个常量,它的值不能被更改
 *     final修饰属性时可以给属性赋值的位置:显示初始化、代码块中初始化、构造器中初始化
 *     final修饰局部变量,当修饰形参时,调用该方法给形参赋值,在方法内只能调用该形参,不能
 *         对它重新赋值
 * 
 * static final:用来修饰属性时,称为全局常量
 */

public class FinalTest {
	
	// final int WIDTH; // The blank final field WIDTH may not have been initialized
	final int WIDTH = 0;
	final int LEFT;
	{
		LEFT = 1;
	}
	
	final int RIGHT;
	public FinalTest() {
		RIGHT = 2;
	}
	public FinalTest(int n) {
		RIGHT = n;
	}
	
//	final int DOWN;
//	public void setDown(int down) { // 当调用完构造器,对象就被创建,此时该对象的所有
								// 属性都应该有值。如果一直没调用这个方法,那final修饰
								// 的DOWN就一直没值
//		this.DOWN = down;
//	}
	
	public void doWidth() {
		// WIDTH = 1; // The final field FinalTest.WIDTH cannot be assigned
	}
	
	public void show() {
		final int NUM;
		// NUM += 20;
	}
	public void show(final int NUM) {
		// NUM = 20; // 调用该方法给NUM赋值,可以调用这个常量的值,但不能在方法内部再给赋值
		System.out.println(NUM);
	}
	
	public static void main(String[] args) {
		FinalTest test = new FinalTest();
		test.show(10); // 10
	}
}

final class FinalA {
	
}

//class B extends FinalA { // The type B cannot subclass the final class FinalA

//}

class AA {
	public final void show() {
		
	}
}
class BB extends AA {
//	public void show() { // Cannot override the final method 
//		 from AA
//		
//	}
}