------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
static关键字
用于修饰成员(成员变量和成员方法)
被修饰后的成员具备以下特点:
1. 随着类的加载而加载
2. 优先于对象存在
3. 被所有对象所共享
4. 可以被类名直接调用
使用注意:
1. 静态方法只能访问静态成员
2. 静态方法中不可以写this,super关键字
3. 主函数是静态的
多个对象当中存在着共同数据
对象建立的越多,因为人的姓名可能不同,但是国家可能是一样的,所以会产生的共同的
contry; 比较浪费内存空间
这时,我们可以单独建立一个东西,把它放在其中,每个Person想调用都可以,没有必要每一个都创建一次,所以就引入了关键字:static
class Person
{
String name;
String contry;
public void show(){
System.out.println("name:"+name+"-->contry:"+contry);
}
}
public class StaticDemo
{
public static void main(String[] args){
Person p=new Person();
p.name="hello";
p.contry="cn";//在某一个国家的人,国籍基本都是相同的,没有必要每次一实例对象都进行赋值
p.show();
}
}
static:一个修饰符,只修饰成员
静态所修饰的内容被对象所共享
当成员被静态修饰后,就多了一种调用的方式,除了可以被对象调用外,还可以被类直接调用à用法: 类名.静态成员
static的缺点:
一个教学班:入学时可以每人分配一个电脑,也可以每人都分配一个饮水机,但是每人一个饮水机会特别的占空间
特有的内容随着对象存储(在堆内存中)
除了栈内存和堆内存之外,还有一个存放数据的空间,叫做方法区(共享区,数据区)
String name; //成员变量,实例变量(对象变量:对象存在变量存在,对象消失,变量消失)
static String country ;//静态的成员变量,类变量;
为什么不把所有的变量都定义成静态的?
1. 一些共同的东西可以定义成static ,但是不同的,独有的不能定义为static
2. 如果全部定义为static,那么它会在类存在时创建,在类销毁时销毁,会浪费内存空间(非静态的变量会在对象实例化的时候创建,在对象销毁时销毁)
static的特点:
1. 随着类的加载而加载,随着类的消失而消失
它的声明周期最长
2. 优先于对象存在,静态是先存在的,对象是后存在的
3. 被所有的对象共享
4. 被所有的对象而调用
实例变量和类变量的区别:
1. 存放位置:
a. 类变量随着类的加载而加载,存在于方法区中
b. 实例变量随着对象的建立而存在于堆内存中
2. 生命周期:
a. 类变量生命周期最长,随着类的消失而消失
b. 实例变量生命周期随着对象的消失而消失
静态使用的注意事项:
class Person
{
String name;
String contry="cn";
Public static void show(){
System.out.println("name:"+name);
}
}
public class StaticDemo
{
public static void main(String[] args){
Person.show();
}
}
编译时会报错:实例成员随着对象的存在而存在.因为使用类直接调用静态方法,所以没有对象的存在,所以name不会存在
静态有利有弊:
利: 对对象的共享数据提供单独的存储,节省空间.没有必要每一个对象都存储一份
可以直接被类名调用
弊: 生命周期过长,访问出现局限性(静态虽好,只能访问静态)
主函数:
是一个特殊的函数,作为程序的入口,被jvm直接调用
主函数的定义:
1. public :代表着该函数的访问权限是最大的
2. static :代表着主函数随着类的加载已经存在了
3. void :主函数没有具体的返回值
4. main:不是关键字,是一个特殊的单词,可以被jvm识别
5. String[] args:函数的参数,参数类型是一个数组,该数组的元素是字符串
6. 主函数是固定格式的,jvm识别
Jvm在调用主函数时,传入的是new string[0] //零个元素,不能打印args[0](这表示一个元素)
主函数传参:
1.在执行时传递参数
class MainDemo
{
public static void main(String[] args)
{
System.out.println(args.length);
for(int i=0;i<args.length;i++){
System.out.println(args[i]);
}
}
}
2.使用类进行传递
class MainDemo
{
public static void main(String[] args)
{
System.out.println(args.length);
for(int i=0;i<args.length;i++){
System.out.println(args[i]);
}
}
}
class MainTest
{
public static void main(String[] args){
String[] arr={"hehe","lala","bao"};
MainDemo.main(arr);
}
}
什么时候使用静态:要从两方面下手:
因为静态修饰的内容有成员变量和函数,那么什么时候使用静态变量(类变量)呢?
1. 当对象中出现共享数据时,该变量被静态所修饰
拿姓名来说:它是共享属性,不是共享数据,共享数据是值相同
2. 对象中的特有数据要定义成非静态,存放在堆内存中
什么时候定义静态函数:
1. 当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的
静态的应用:
每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装,以便复用
虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作
发现的问题:
1. 对象是用于封装数据的,可是ArrayTool对象并未封装特有数据
2. 操作数组的每一个方法都没有用到ArrayTool对象中的特有数据
这时就考虑,让程序更严谨,是不需要对象的.
可以将ArrayTool中的方法都定义成static.直接通过类名调用即可
将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象
为了更加严谨,强制让该类不能建立对象
可以通过将构造函数私有化完成
数组工具类:
class ArrayTool
{
private ArrayTool(){}
public static int getMax(int[] arr){
int max=0;
for(int i=1;i<arr.length;i++){
if(arr[i]>arr[max]){
max=i;
}
}
return max;
}
public static void selectArray(int[] arr){
for(int i=0;i<arr.length-1;i++){
for(int j=i+1;j<arr.length;j++){
if(arr[i]>arr[j]){
swap(arr,i,j);
}
}
}
}
private static void swap(int[] arr,int x,int y){
if(arr[x]>arr[y]){
int temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
}
public static void printArray(int[] arr){
System.out.print("[");
for(int i=0;i<arr.length;i++){
if(i==arr.length-1)
System.out.print(arr[i]+"]");
else
System.out.print(arr[i]+"'");
}
}
}
调用:
class ArrayToolDemo
{
public static void main(String[] args){
int arr[]={1,2,4,3,9,8};
ArrayTool.selectArray(arr);
ArrayTool.printArray(arr);
}
}
虽然都在一个文件下,但是出现了问题:说找不到ArrayTool文件
这是就需要设置classpath:
但是这里有个小瑕疵,如果连个运行文件不再同一个文件夹里,也会找不到,所以需要加 .;表示当前文件下
一个类中默认会有一个空参数的构造函数
这个默认的构造函数的权限和所属的类一致
如果类被public修饰,那么默认的构造函数也带public修饰符
如果类没有被public修饰,那么默认的构造函数也没有public修饰
也就是说:默认的构造函数的权限随着类的变化而变化
静态代码块:
static{
}
特点:随着类的加载而加载只执行一次,并优先于主函数
用于给类进行初始的
注意事项: 当一个类中没有实际操作时,内存中不会加载此类
1. 后面不为null
2. new (实际调用的是类的构造方法)
3. 调用里面的静态方法
4. 调用里面的一般方法
class Person
{
static {
System.out.println("a");
}
public static void show(){
System.out.println("show run");
}
}
public class StaticDemo
{
public static void main(String[] args){
Person p=null;
}
}
静态代码块: 给类初始化的
构造代码块: 给对象初始化的
构造函数: 给对应对象初始化的
public class Test {
static int age;
static {
System.out.println("这是静态代码块");
age+=10;
System.out.println("静态代码块-->age:"+age);
}
{
System.out.println("这是代码块");
age+=20;
System.out.println("代码块-->age:"+age);
}
public Test(){
System.out.println("这是构造方法");
age+=30;
System.out.println("构造函数-->age:"+age);
}
public static void main(String[] args) {
new Test();//第一步,在执行第二步是注释掉
// System.out.println(Test.age);
}
}
执行结果:
这是静态代码块
静态代码块-->age:10
这是代码块
代码块-->age:30
这是构造方法
构造函数-->age:60
Person p =new Person();
前半部分:Person p 会在栈内存中建立一个引用
后半部分:new Person() 通过jvm的加载器把Person.class文件加载到内存中,如果有静态代码块会现在方法区中开辟,并在堆内存中开辟空间,存储对象在new的过程中:
1. 先是默认初始化;
2. 显示初始化;
3. 之后是构造代码块初始化
单例设计模式:解决一个类在内存中只存在一个对象
设计模式:从该类的哪里获取的
想要保证唯一性:
1. 为了避免其他程序过多建立该对象,先禁止其他程序建立该对象
2. 还为了让其他程序可以访问到该类对象,只好在本类中自定义一个对象
3. 为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式
如何用代码体现?
1. 私有化构造函数
2. 在类中创建一个本类对象
3. 提供一个方法可以获取到该对象
对于事物该怎么描述,还怎么描述.
当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可
这个是先初始化对象:称为 饿汉式(在类加载到内存中时就初始化)
class Single
{
private static final Single s=new Single(); //加上final更加严谨
private Single(){}
public static getInstance(){
return s;
}
}
方法二 :(延时加载)
对象被调用时,才初始化,称为: 懒汉式
class Single
{
private static Single s;
private Single(){}
public static void getInstance(){
if(s==null){
s=new Single():
}
return s;
}
}
此方法在多线程调用时会出现安全问题:
解决办法:同步
解决方法一:同步方法:
class Single
{
private static Single s;
private Single(){}
public static synchronized void getInstance(){
if(s==null){
s=new Single():
}
return s;
}
}
这样做比较低效,因为每一次调用都要加锁解锁
解决方法二:同步代码块:
class Single
{
private static Single s;
private Single(){}
public static void getInstance(){
if(s==null){
synchronized(Single.clss){
if(s==null)
s=new Single()
}
}
return s;
}
}
解释:
假如有A,B,C三个线程在执行:
首先A进入为null,判断为null进入同步代码块 上锁,在判断为null,挂掉了
然后B进入为null,判断为null(因为A在实例化之前下了CPU),但是A已经进入同步代码跨,B进不去,这时A醒了,实例化了Single,并解锁 return .B再进入同步代码快 上锁,判断不为null,解锁 return .
最后C进入已经不为空了 直接return注意:
开发一般使用饿汉式,安全