Java面向对象(十三)—— final,static关键字、匿名对象、内部类、包的声明与访问、访问修饰符、代码块

目录

第一章   final关键字

1.1 final的概念

1.2 final的特点

第二章 static关键字

1.static概念

2.2 static特点

2.3 static注意事项

第三章  匿名对象

3.1 匿名对象的概念

3.2 匿名对象的特点

第四章 内部类

4.1 内部类概念

4.2成员内部类

4.3局部内部类

4.4 内部类的实际使用——匿名内部类

第五章  包的声明与访问

5.1包的概念

5.2包的声明格式

第六章 访问修饰符

第七章 代码块

7.1 局部代码块

7.2构造代码块

7.3静态代码块

第八章 总结


第一章   final关键字

 

1.1 final的概念

继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些类在描述完之后,不想被继承,或者有些类中的部分方法功能是固定的,不想让子类重写。可是当子类继承了这些特殊类之后,就可以对其中的方法进行重写,那怎么解决呢?

要解决上述的这些问题,需要使用到一个关键字final,final的意思为最终,不可变。final是个修饰符,它可以用来修饰类,类的成员,以及局部变量

 

1.2 final的特点

1. final修饰类不可以被继承,但是可以继承其他类。

class Yy {}
final class Fu extends Yy{} //可以继承Yy类
class Zi extends Fu{} //不能继承Fu类

2. final修饰的方法不可以被覆盖,但父类中没有被final修饰方法,子类覆盖后可以加final。

class Fu {
	// final修饰的方法,不可以被覆盖,但可以继承使用
    public final void method1(){}
    public void method2(){}
}
class Zi extends Fu {
	//重写method2方法
	public final void method2(){}
}

3. final修饰的变量称为常量,这些变量只能赋值一次。

final int i = 20;
i = 30; //赋值报错,final修饰的变量只能赋值一次

4. 引用类型的变量值为对象地址值,地址值不能更改,但是地址内的对象属性值可以修改。

final Person p = new Person();
Person p2 = new Person();

p = p2; //报错,final修饰的变量p,所记录的地址值不能改变

p.name = "小明";//可以更改p对象中name属性值

5. final修饰的成员变量,需要在创建对象前赋值,否则报错。(当没有显式赋值时,多个构造方法的均需要为其赋值。)

构造方法,是创建对象中的事情,可以为final修饰的成员变量赋值。

setXXX方法,是创建对象之后的事情,不能为final修饰的成员赋值。

package day13.demo1;

class Demo {
	//直接赋值
	final int m = 100;
	
	//final修饰的成员变量,需要在创建对象前赋值,否则报错。
	final int n; 
	public Demo(){
		//可以在创建对象时所调用的构造方法中,为变量n赋值
		n = 2016;
	}
}

 

第二章 static关键字

 

1.static概念

此例中,每个对象的name,age都不同,但schoolName都相同,若每个对象都开辟三个变量地址来存储就发生了浪费。因此解决办法就是在成员变量schoolName前面加上static修饰符,将其变为共享数据。

 

2.2 static特点

被static修饰的成员变量属于类,不属于这个类的某个对象。(也就是说,多个对象在访问或修改static修饰的成员变量时,其中一个对象将static成员变量值进行了修改,其他对象中的static成员变量值跟着改变,即多个对象共享同一个static成员变量)

被static修饰的成员可以并且建议通过类名直接访问。

访问静态成员的格式:

  • 类名.静态成员变量名
  • 类名.静态成员方法名(参数)
  • 对象名.静态成员变量名       ------不建议使用该方式,会出现警告
  • 对象名.静态成员方法名(参数)     ------不建议使用该方式,会出现警告
package day13.demo2;
/*
 *   定义Person类,
 *   定义对象的特有数据,和对象的共享数据
 *   对象的特有数据(非静态修饰) 调用者只能是 new 对象
 *   对象的共享数据(静态修饰)  调用者可以是new 对象,可以是类名
 *   
 *   被静态修饰的成员,可以被类名字直接调用
 */
public class Person {
	String name;
	
	static String className;
}
package day13.demo2;

public class Test {
	public static void main(String[] args) {
		System.out.println(Person.className);
		Person p1 = new Person();
		Person p2 = new Person();
		
		p1.name = "哈哈";
		p2.name = "嘻嘻";
		System.out.println(p1.name);
		System.out.println(p2.name);
		
		//对象调用类的静态成员变量
		p1.className = "基础班";
		System.out.println(p2.className);
		
	}
}

结果:

 

2.3 static注意事项

  • 静态内容是优先于对象存在,只能访问静态,不能使用this/super。静态修饰的内容存于静态区。因为静态是先于对象的,this是调用本类对象的引用,还没有对象不能调用。super也是没有对象的。
  • 同一个类中,静态成员只能访问静态成员。
  • main方法为静态方法仅仅为程序执行入口,它不属于任何一个对象,可以定义在任意类中。
  • 类进入到方法区,要先将自己的静态属性、静态方法放在静态区以作为共享。
  • 静态属于自己的类。
  • 内存中,静态优先于非静态存在的。

为何运行时的命令是java 类名,而不是java xxx.class ?

命令行编译运行:

编译: javac XXX.java

运行:java 类名

运行时写类名而不是.class,是因为main归类所有。即启动java虚拟机写的是类名而不是文件名。

 

 

内存图解释:

Test.class进入方法区,初始化自己的静态成员main,mian进入数据共享区(静态区),静态属于自己的类,因此这个main归Test类所属。随后Person.class进入方法区。因为Person类有自己的静态成员,因此Person类与className进入静态区。

开始执行,运行main,JVM到静态区将main方法复制一份压栈执行。执行到Person.className时,JVM去静态区找到属于Person类的静态属性className。执行new Person(),去堆内存开辟空间,String name跟随对象进入堆内存默认初始化。


 

在静态中不能调用非静态

为什么呢? 为什么静态不能调用非静态
 因为生命周期,静态优先于非静态存在于内存中

 

static 的生命周期较长,占内存(类进入方法区后,静态就存在了。静态随着类的消失而消失)。比成员变量存在时间长。

 

成员变量加不加static修饰,就看定义事物的时候,多个事物之间是否有共性。

成员方法加不加static修饰,若方法中没有用到非静态成员的,则将此设为静态方法,节省堆内存空间。(调用到静态变量也用静态)

  • 多态调用中,编译看谁,运行看谁
  • 编译都看 = 左边的父类, 父类有编译成功,父类没有编译失败
  • 运行,静态方法, 运行父类中的静态方法
  • 运行,非静态方法,运行子类的重写方法
  • 成员变量,编译运行全是父类
package day13.demo4;

public class Fu {
	static int i = 1;
	
	public static void show(){
		System.out.println("父类静态方法show");
	}
}


package day13.demo4;

public class Zi extends Fu{
	static int i = 2;
	public static void show(){
		System.out.println("子类的静态方法show");
	}
}


package day13.demo4;
/*
 *    多态调用中,编译看谁,运行看谁
 *    编译都看 = 左边的父类, 父类有编译成功,父类没有编译失败
 *    运行,静态方法, 运行父类中的静态方法
 *    运行,非静态方法,运行子类的重写方法
 *    成员变量,编译运行全是父类
 *    
 *    
 */
public class Test {

	public static final double PI = 3.14159265358979323846;
	
	public static void main(String[] args) {
		Fu f = new Zi();
//		System.out.println(f.i);
		//调用还是父类的静态方法,原因: 静态属于类,不属于对象
		//对象的多态性,静态和对象无关,父类的引用.静态方法
		f.show();
		System.out.println();
	}
}

 

 

第三章  匿名对象

 

3.1 匿名对象的概念

匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。 

创建一个普通对象

Person p = new Person();

创建一个匿名对象

new Person();

 

3.2 匿名对象的特点

创建匿名对象直接使用,没有变量名。

匿名对象在没有指定其引用变量时,只能使用一次。

匿名对象可以作为方法接收的参数、方法返回值使用。

public class Person {
	public void eat(){
		System.out.println("人在吃饭");
	}
}



/*
 *  有名字对象,引用类型变量,可以反复使用
 *  匿名对象,没有引用变量,只能使用一次
 *  
 *  匿名对象可以当作参数传递
 *  匿名对象可以当作方法的返回值
 */

public class Test {
	public static void main(String[] args) {
		Person p = new Person();
		p.eat();
		
		new Person().eat();
		new Person().eat();
		
		method(new Person());
		
		Person p1 = method();
		p1.eat();
	}
	
	//方法返回值是Person类型
	//方法return语句,返回的是这个类的对象
	public static Person method(){
		//Person p = new Person();
		return new Person();
	}
	
	
	//调用方法method,传递Person类型对象
	public static void method(Person p){
		p.eat();
	}
}

 

第四章 内部类

 

4.1 内部类概念

什么是内部类?

将类写在其他类的内部,可以写在其他类的成员位置和局部位置,这时写在其他类内部的类就称为内部类。其他类也称为外部类。

什么时候使用内部类?

在描述事物时,若一个事物内部还包含其他可能包含的事物,比如在描述汽车时,汽车中还包含这发动机,这时发动机就可以使用内部类来描述。

class 汽车 { //外部类

    class 发动机 { //内部类

    }

}

内部类的分类:

内部类分为成员内部类与局部内部类。

我们定义内部类时,就是一个正常定义类的过程,同样包含各种修饰符、继承与实现关系等。在内部类中可以直接访问外部类的所有成员。

 

4.2成员内部类

成员内部类,定义在外部类中的成员位置。与类中的成员变量相似,可通过外部类对象进行访问。

定义格式:

class 外部类 {

    修饰符 class 内部类 {

        //其他代码

         }

}

访问方式:

外部类名.内部类名 变量名 = new 外部类名().new 内部类名();

 

4.3局部内部类

局部内部类,定义在外部类的方法中。访问方法与局部变量相似,可通过调用方法进行访问。

定义格式:

class 外部类 {
    修饰符 返回值类型 方法名(参数) {
    class 内部类 {
    //其他代码
      }    
   }
}

访问方式:

在外部类方法中,创建内部类对象,进行访问。

package day13.demo5;
/*
 *   内部类的定义
 *     将内部类,定义在了外部的成员位置
 *   类名Outer,内部类名Inner
 *   
 *   成员内部类,可以使用成员修饰符,public static ....
 *   也是个类,可以继承,可以实现接口
 *   
 *   调用规则: 内部类,可以使用外部类成员,包括私有
 *   外部类要使用内部类的成员,必须建立内部类对象
 */
public class Outer {
	int i = 1;
	class inner{
		int i =2;
		public void inner() {
			int i = 3;
			System.out.println("内部类方法inner "+i);//3,就近原则
			System.out.println(this.i);//2,本类(inner)的成员变量
			System.out.println(Outer.this.i);//1,Outer类的成员变量
		}
		
	}
}
package day13.demo5;

public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Outer.inner in = new Outer().new inner();
		in.inner();
	}

}

 

4.4 内部类的实际使用——匿名内部类

内部类是为了应对更为复杂的类间关系。查看源代码中会涉及到,而在日常业务中很难遇到,这里不做赘述。

最常用到的内部类就是匿名内部类,它是局部内部类的一种。

定义的匿名内部类有两个含义:

临时定义某一指定类型的子类

定义后即刻创建刚刚定义的这个子类的对象

抽象类不可以直接new 因此格式为: new 接口(){ };

package day13.demo7;
/*
 * 实现类,实现接口 重写接口抽象方法,创建实现类对象
 * 创建对象的写法
 * class XXX implments Smoking{
 * 	public void smoking(){
 * 
 * 	}
 * }
 * XXX x = new XXX();
 * x.smoking();
 * Smoking s = new XXX();
 * s.smoking();
 */
public interface Smoking {
	public abstract void smoking();
}
package day13.demo7;
/*
 * 使用匿名内部类
 *  定义实现类,重写方法,创建实现类对象,一步搞定
 *  格式:
 *  new 接口或者父类(){重写抽象方法};
 *  抽象类不可以直接new 因此格式为 new 接口(){};
 *  从new开始,到分号结束,就是创建了接口的实现类的对象
 *  
 */
public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//使用匿名内部类
		new Smoking() {
			public void smoking() {
				System.out.println("人在吸烟");
			}
			}.smoking();
	}
}
/*
 *class XXX implments Smoking{
 * 	public void smoking(){
 * 
 * 	}
 * }
 * XXX x = new XXX();
 * x.smoking();
 * 两种方法等同,只是匿名内部类代码量少
*/

匿名对象一次只能调用一个方法,那如何同时调用多个方法呢?

 定义并创建该父类的子类对象,并用多态的方式赋值给父类引用变量。

package day13.demo7;

public abstract class Animal {
	public abstract void eat();
	public abstract void sleep();
}

package day13.demo7;
/*
 *    new Animal(){
			public void eat(){
				System.out.println("在吃饭");
			} 
			public void sleep(){
				System.out.println("在睡觉");
			}
		 };
	以上代码,就是Animal的子类的对象
	多态性, 父类引用 = 子类的对象

 */
public class Test2 {
	public static void main(String[] args) {
		//定义并创建该父类的子类对象,并用多态的方式赋值给父类引用变量
		Animal a= new Animal(){
			public void eat(){
				System.out.println("在吃饭");
			} 
			public void sleep(){
				System.out.println("在睡觉");
			}
		 };
		 a.eat();
		 a.sleep();
	}
}

 

第五章  包的声明与访问

 

5.1 包的概念

java的包,其实就是我们电脑系统中的文件夹,包里存放的是类文件。

当类文件很多的时候,通常我们会采用多个包进行存放管理他们,这种方式称为分包管理。

在项目中,我们将相同功能的类放到一个包中,方便管理。并且日常项目的分工也是以包作为边界。

通俗的说包就是文件夹,对类文件进行管理。

 

5.2 包的声明格式

通常使用公司网址反写,可以有多层包,包名采用全部小写字母,多层包之间用”.”连接

 

第六章 访问修饰符

 

public

protected

default

private

同一类中

同一包中(子类与无关类)

 

不同包的子类

 

 

不同包中的无关类

 

 

 

归纳一下:在日常开发过程中,编写的类、方法、成员变量的访问

  • 要想仅能在本类中访问使用private修饰;
  • 要想本包中的类都可以访问不加修饰符即可;
  • 要想本包中的类与其他包中的子类可以访问使用protected修饰
  • 要想所有包中的所有类都可以访问使用public修饰。
  • 注意:如果类用public修饰,则类名必须与文件名相同。一个文件中只能有一个public修饰的类。

 

面试题: 受保护权限本包内的类与子类可以调用。但子类如何调用?

子类对象调用会报错。受保护权限,只能是在子类定义的方法的里面调用。原因是隐式语句super()调用父类的受保护成员。

day13.demo8包: 

package day13.demo8;

public class A {
	int i = 1;
	protected void abc(){
		System.out.println("ABC");
	}
}


package day13.demo8;

public class C {
	public void show(){
		new A().abc();//同在day13.demo8下,保护类可以直接调用
	}
}

day13.demo9包:

package day13.demo9;
import day13.demo8.*;//调包,因为A类在day13.demo8包中定义

public class B extends A{
	public void show(){
	  //受保护权限,只能是子类的里面(B类的方面里面) 调用父类的受保护成员
		// super
		abc();//正确
	}
	public void show1() {
		abc();//正确
	}
	//abc();//报错
}


package day13.demo9;

public class Test {
	public static void main(String[] args) {
	 B b = new B();
	 //b.abc();//报错,不能创建子类对象调用
     b.show();
	 b.show1();
	}
}

结果:

 

 

第七章 代码块

 

7.1 局部代码块

局部代码块是定义在方法或语句中的。

特点:

以”{}”划定的代码区域,此时只需要关注作用域的不同即可

方法和类都是以代码块的方式划定边界的

class Demo{
	public static void main(String[] args)	{
		{
         		 int x = 1;
        		 System.out.println("普通代码块" + x);
		}
		int x = 99;
		System.out.println("代码块之外" + x);
	}
}

 

7.2构造代码块

构造代码块是定义在类中成员位置的代码块。

特点:

优先于构造方法执行,构造代码块用于执行所有对象均需要的初始化动作

每创建一个对象均会执行一次构造代码块。

 

7.3静态代码块

静态代码块是定义在成员位置,使用static修饰的代码块。

特点:

优先于主方法执行、优先于构造代码块执行,当以任意形式第一次使用到该类时执行

该类不管创建多少对象,静态代码块只执行一次

可用于给静态变量赋值,用来给类进行初始化。

package day13.demo7;
/*
 *  静态代码块, 只执行一次
 *  构造代码块,new一次,就执行一次,优先于构造方法
 *  构造方法, new 一次,就执行一次
 */
public class Person {
  private String name;
  private int age;
  

  public Person(String name,int age){
	  this.age = age;
	  this.name = name;
	  System.out.println("我是构造方法");
  }
  //构造代码块
  {
	  System.out.println("我是构造代码块");
  }
  
  //静态代码块
  static{
	  System.out.println("我是静态代码块");
  }
}
package day13.demo7;

public class Test3 {
	public static void main(String[] args) {
		new Person("张三",20);
		new Person("张三2",220);
	}
}

 运行结果:

 

静态代码块, 只执行一次
构造代码块,new一次,就执行一次,优先于构造方法。
构造方法, new 一次,就执行一次 

 

第八章 总结

final:关键字,最终的意思

                  final修饰的类:最终的类,不能被继承

                  final修饰的变量: 相当于是一个常量, 在编译生产.class文件后,该变量变为常量值

                  final修饰的方法: 最终的方法,子类不能重写,可以继承过来使用

 

static : 关键字, 静态的意思

                  可以用来修饰类中的成员(成员变量,成员方法)

                  注意: 也可以用来修饰成员内部类

特点:

                           被静态所修饰的成员,会被其对象所共享

                           被静态所修饰的成员,可以通过类名直接调用,方便

                                    Person.country = "中国";

                                    Person.method();

注意事项:

                           静态的成员,随着类的加载而加载,优先于对象存在

                           在静态方法中,没有this、super关键字

                           静态方法中,只能调用静态的成员(静态成员变量,静态成员方法

 

匿名对象:一个没有名字的对象

特点:

创建匿名对象直接使用,没有变量名

匿名对象在没有指定其引用变量时,只能使用一次

匿名对象可以作为方法接收的参数、方法返回值使用

 

内部类:在一个类中,定义了一个新类,这个新的类就是内部类   

                  class A {//外部类

                           class B{// 内部类

                           }

                  }

特点:

            内部类可以直接访问外部类的成员,包含私有的成员。

包的声明与访问

类中包的声明格式:

package 包名.包名.包名…;

带有包的类,创建对象格式:包名.类名 变量名 = new包名.类名();

导包的格式:

import 包名.类名

权限修饰符

                  public : 公共的

                  protected: 受保护的

                  private : 私有的

代码块:

                  局部代码块:定义在方法中的,用来限制变量的作用范围

                  构造代码块:定义在类中方法外,用来给对象中的成员初始化赋值

                  静态代码块:定义在类中方法外,用来给类的静态成员初始化赋值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值