JAVA基础(十二)

本文详细介绍了Java中的static关键字,包括静态变量、静态方法和静态代码块的特性及应用场景。同时,讲解了单例设计模式,探讨了饿汉式和懒汉式的优缺点,以及单例模式在系统性能优化中的作用。最后,提到了main方法的语法和形参的使用方式。
摘要由CSDN通过智能技术生成

关键字:static

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上 的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象, 其方法才可以供外部调用。

我们有时候希望无论是否产生了对象或无论产生了多少 对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个 国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中 都单独分配一个用于代表国家名称的变量。

使用static修饰属性:静态变量

使用范围:

        在Java类中,可用static修饰属性、方法、代码块、内部类

被修饰后的成员具备以下特点:

        随着类的加载而加载

        优先于对象存在

        修饰的成员,被所有对象所共享

        访问权限允许时,可不创建对象,直接被类调用

        类中的常量也常常声明为static 

        由于类只会加载一次,则静态变量在内存也只会存在一份存放在方法区的静态域中

        属性的分类:按是否使用static修饰,又分为:静态属性和非静态属性(实例变量)。

                实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性,当修改其中一个对象的非静态属性时,不会导致其他对象中同样的属性值的修改。                                                                           

                静态变量:我们创建了类的多个对象,多个对象共享一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过的                                       

 如果想让一个类的所有实例共享数据,就用类变量!

静态结构使用例:

System.out 、Math.PI

 使用static修饰方法:静态方法

        随着类的加载二加载,可以通过类.静态方法的方式进行调用

        静态方法中只能调用静态的方法或属性

        非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性

        静态方法中不能使用this和super关键字

类属性、类方法的设计思想

类属性作为该类各个对象之间共享的变量。在设计类时,分析哪 些属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。

如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。

操作静态属性的方法通常设置为静态

工具类的方法习惯上声明为static

测试代码

package com.xxx.java;

public class StaticTest {
	public static void main(String[] args) {
		
		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);
		
		Chinese.show();
		//Chinese.eat();
	}
}

class Chinese{
	
	String name;
	int age;
	static String nation;
	
	public void eat() {
		System.out.println("吃中餐");
	}
	
	public static void show() {
		System.out.println("我是一个中国人");
		//eat();
		//name = "Tom";
		//可以调用静态的结构
		nation = "中国";
		walk();
	}
	
	public static void walk() {
		System.out.println("走路");
	}
}

ArrayUtil工具类优化

package com.xxx.java;

//自定义数组工具类

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

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

	// 求数组的总和
	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, j = arr.length - 1; i < j; i++, j--) {
			int temp = arr[i];
			arr[i] = arr[j];
			arr[j] = temp;
		}
	}

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

	// 数组排序
	public static void sort(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			for (int j = 0; j < arr.length - 1 - i; j++) {
				if (arr[j] > arr[j + 1]) {
					swap(arr,j,j+1);
				}
			}
		}
	}

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

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

		for (int i = 0; i < arr.length; i++) {
			if (dest == arr[i]) {
				return i;
			}
		}
		return -1;
	}
	//交换数组中指定两个位置元素的值
	private static void swap(int[]arr,int i,int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
}

测试

package com.xxx.java;

public class ArrayUtilTest {
	public static void main(String[] args) {
		//ArrayUtil util = new ArrayUtil();
		int[] arr = {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();
//		System.out.println("排序后");
//		ArrayUtil.print(arr);
		
		System.out.print("查找:");
		int index = ArrayUtil.getIndex(arr, -5);
		if (index >= 0) {
			System.out.println(index);
		} else {
			System.out.println("没找到");
		}
	}
}

相关应用

package com.xxx.java;

public class CircleTest {
	public static void main(String[] args) {
		Circle c1 = new Circle();
		Circle c2 = new Circle();
		Circle c3 = new Circle(3.4);
		
		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;

	private static int total;
	private static int init = 1001;
	
	
	
	public Circle() {
		super();
		id = init++;
		total++;
	}



	public Circle(double radius) {
		this();
		this.radius = radius;
		//id = init++;
		//total++;
	}



	public static int getTotal() {
		return total;
	}



	public double getRadius() {
		return radius;
	}



	public void setRadius(double radius) {
		this.radius = radius;
	}



	public int getId() {
		return id;
	}



	public double findArea() {
		return 3.14 * radius * radius;
	}
}

相关练习

编写一个类实现银行账户的概念,包含的属性有“帐号”、“密 码”、“存款余额”、“利率”、“最小余额”,定义封装这些 属性的方法。账号要自动生成。

编写主类,使用银行账户类,输入、输出3个储户的上述信息。 考虑:哪些属性可以设计成static属性。

package com.xxx.exer;

public class Account {
	private int id;
	private String pwd = "000000";
	private double balance;

	private static double interestRate;
	private static double minMoney;
	private static int init;
	
	

	public Account() {
		super();
		id = init++;
	}
	

	public Account(String pwd, double balance) {
		this();
		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;
	}


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

}

测试

package com.xxx.exer;

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

单例 (Singleton)设计模式

设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。”套路”

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对 某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构 造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生 类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无 法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象, 静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象 的变量也必须定义成静态的。

饿汉式:

package com.xxx.java1;

public class SingletonTest1 {
	public static void main(String[] args) {
		Bank bank1 = Bank.getInstance();
		Bank bank2 = Bank.getInstance();
		
		System.out.println(bank1 = bank2);
	}
	
}
//饿汉式
class Bank{
	//私有化构造器
	private Bank(){
		
	}
	//内部创建类的对象
	//要求声明为静态,否则静态方法中无法调用
	private static Bank instance = new Bank();
	
	//提供public的方法,返回类的对象
	public static Bank getInstance() {
		return instance;
	}
}

懒汉式:

package com.xxx.java1;

public class SingletonTest2 {
	public static void main(String[] args) {
		Order order1 = Order.getInstance();
		Order order2 = Order.getInstance();
		
		System.out.println(order1 == order2);
	}
}
//懒汉式
class Order {
	//私有化类的构造器
	private Order() {

	}
	//声明当前类对象,不进行初始化
	private static Order instance = null;
	
	//声明public static的返回当前类对象的方法
	public static Order getInstance() {
		//不判断会每次执行时都新生成一个对象实例
		if(instance == null) {
			instance = new Order();
		}
		return instance;
	}
}

区分饿汉式和懒汉式:

        懒汉式:

                优点:延迟对象的创建

                缺点:线程不安全——到多线程时修改

        饿汉式:

                优点:饿汉式是线程安全的

                缺点:对象加载时间过长

单例模式的优点:

        由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的 产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决

应用场景

 网站的计数器,一般也是单例模式实现,否则难以同步。

 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。

 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。

 Application 也是单例的典型应用

 Windows的Task Manager (任务管理器)就是很典型的单例模式

 Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

理解main方法的语法

说明

        作为程序的入口

        是一个普通的静态方法

public:由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public

static:又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的

void:main方法执行完后程序就结束了,不需要返回值

形参:该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行               的类的参数。

形参的使用方式(Eclipse)

1.先run as一遍代码

2.选择Run Configurations 

 3.左侧选择当前编译好的字节码文件,右侧选择Arguments

在Program arguments框内输入字符串,空格隔开,可以不加双引号。Run运行程序 

执行结果如下

 相关代码

package com.xxx.java1;

public class MainDemo {
	public static void main(String[] args) {
		for (int i = 0; i < args.length; i++) {
			System.out.println("***********" + args[i]);
		}
	}
}	

若是其他基本数据类型,可以使用包装类的parseXxx方法 

package com.xxx.java1;

public class MainTest {
	public static void main(String[] args) {
		Main.main(new String[100]);
	}
}

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]);
		}
	}
}

 形参的使用方式(控制台)

1、先删掉包名,在编译javac xxx

2、java xxx 形参

代码块(或初始化块)

代码块(或初始化块)的作用:对Java类或对象进行初始化。

代码块(或初始化块)的分类:

        一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块 (static block),没有使用static修饰的,为非静态代码块。         

static代码块通常用于初始化static的属性

静态代码块:

        可以有输出语句。

        初始化类的信息。

        不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。

        若有多个静态的代码块,那么按照从上到下的顺序依次执行。

        静态代码块的执行要先于非静态代码块。

        静态代码块随着类的加载而加载,且只执行一次。

非静态代码块:

        可以有输出语句。

        可以在创建对象时,对对象的属性进行初始化。

        除了调用非静态的结构外,还可以调用静态的变量或方法。

        若有多个非静态的代码块,那么按照从上到下的顺序依次执行。

        每次创建对象的时候,都会执行一次。且先于构造器执行。

代码

package com.xxx.java2;

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() {

	}

	public Person(String name,int age) {
		this.name = name;
		this.age = age;
	}
	
	//代码块
	static {
		System.out.println("hello,static block-1");
		desc = "我是一个爱学习的人";
		info();
	}
	static {
		System.out.println("hello,static block-2");
	}
	
	{
		System.out.println("hello,block-1");
		age = 1;
	}
	{
		System.out.println("hello,block-2");
	}
	//方法
	public void eat() {
		System.out.println("吃饭");
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
	public static void info() {
		System.out.println("我是一个快乐的人");
	}
}

使用示例

        例1

package com.xxx.java2;
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();
	}
}

解析:

先加载父类和间接父类,然后加载每个类静态代码块即:

        加载Object的静态代码块(无)

        加载Root的静态代码块

        加载Mid的静态代码块

        加载Leaf的静态代码块

之后对象创建完毕,先加载父类和间接父类的构造器,而此时非静态代码块先于父类构造器执行:

        加载Object的代码块(无),加载Object的构造器(无内容)

        加载Root的代码块,加载Root的构造器

        加载Mid的代码块,加载Mid的无参构造器,加载Mid的带参构造器

        加载Leaf的代码块,加载Leaf的构造器        

若再次new Leaf(),静态代码块内容不在执行

        例2

package com.xxx.java2;

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();
	}

}

解析: 

        main方法是由当前main所在的类通过类.main调用的,所有先初始化当前类以及他的父类和间接父类             

        当前main方法在Son中,由Son.main()调用,要初始化Son就要先初始化Son的父类和间接父类         

        初始化Object

        初始化Father(静态代码块)

        初始化Son(静态代码块)

后续执行同例1

结果为:1 4 7 * 2 3 5 6 * 2 3 5 6 * 2 3

对属性的赋值顺序

1.默认初始化

2.显式初始化 / 3在代码块中赋值

4构造器中初始化

5对象.属性或对象.方法的方式赋值

执行顺序1——2 / 3——4——5

package com.xxx.java2;

public class OrderTest {
	public static void main(String[] args) {
		Order o = new Order();
		System.out.println(o.orderId);
	}
}
 
class Order{
//	int orderId = 3;
//	{
//		orderId = 4;
//	}
	{
		orderId = 4;
	}
	int orderId = 3;
	
}

关键字:native

native关键字声明调用底层c或c++的方法

关键字:final

在Java中声明类、变量和方法时,可使用关键字final来修饰。

        final标记的类不能被继承。

                内置的final类:String、System、StringBuffer

        final标记的方法不能被子类重写。

                内置的funal方法:Object的getClass()

final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。

        final修饰属性,可以赋值的位置有

                显式初始化、在代码块中赋值、在构造器中赋值

        final修饰局部变量:为常量,无法修改

                      修饰形参:表示此形参是一个常量,只能在方法内使用该形参,不能对其进行修改

        static final:修饰属性 全局常量

package com.xxx.java2;

public class FinalTest {

	// final int UP; 无法默认初始化赋值
	final int WIDTH = 10; // 可以显式初始化赋值
	// 可以在代码块内赋值
	final int LEFT;

	{
		LEFT = 1;
	}
	// 可以在构造器中赋值,但每个构造器中都必须赋值
	final int RIGHT;

	public FinalTest() {
		RIGHT = 2;
	}

	public FinalTest(int n) {
		RIGHT = n;
	} 
	//不可以在方法中赋值
//	final int DOWN;
//
//	public int setDOWN(int down) {
//		DOWN = down;
//	}

	// public void doWidth() {
//		WIDTH = 20;
//	}
	
	public void show() {
		final int NUM = 10;	//常量
		//NUM += 1;
	}
	public void show(final int num) {
		//num = 5;
		System.out.println(num);
		
	}
	public static void main(String[] args) {
		FinalTest test = new FinalTest();
		//test.setDOWN(5);
		test.show(10);
	}
}

final class FinalA {

}

//class B extends FinalA{
//	
//}

//class C extends String{
//	
//}

class AA {
	public final void name() {

	}
}

class BB extends AA {
//	public void name() {
//		
//	}
}

练习:

1

public class Something {
    public int addOne(final int x) {
        return ++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++;        //可以,地址没有变
    }
}

class Other {
    public int i;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值