oopDay03 final、static与抽象
final、static与抽象
final 关键字
final 修饰变量
final概述
在 Java中,final 可以修饰变量,修饰方法,修饰类
- final 修饰的变量:可以初始化,不能在更改
- final修饰的方法:不能在子类中重写
- final 修饰的类:不能再被继承派生出子类了
final修饰局部变量
在方法中声明的变量称为局部变量
局部变量加final修饰以后,只能初始化一次,不能再次更改
- 可以声明的同时初始化
- 或者在构造函数中初始化
当final修饰引用类型变量时
- 变量的值就是引用对象的地址值,不能再次修改,但是可以改变该地址中的数据
```java
package day03;import java.util.Arrays;
/**
- final修饰局部变量
*/
public class Demo01 {
public static void main(String[] args) {
//在方法中声明的变量就是局部变量
final int a; //声明一个final类型的变量
a = 5; //只能够赋值一次 初始化
System.out.println(a);
// a = 10; //再次赋值时 会报错 (已初始化 final修饰的局部变量,不能再次修改值)
final int b = 10; //声明变量的同时初始化
//b = 10 ; //编译错误 不能再次赋值
//final 修饰引用类型变量
//变量中存的是引用类型的首地址 初始化之后不可修改
final int[] arr = {5,10};
System.out.println(Arrays.toString(arr));
arr[0] = 15; //final 修饰的引用类型的局部变量中的数据可以改变
System.out.println(Arrays.toString(arr));
//arr = new int[10];//编译错误 final修饰的arr的引用不能改变
final Aoo aoo = new Aoo();
//aoo = new Aoo()
aoo.c = 10;
System.out.println(aoo.c);
// aoo = null; aoo引用中保存的是地址 不能修改
}
}
class Aoo{
int c = 5;}
3. final修饰方法的参数
- Java中方法的参数也是一种局部变量,只是其声明位置是方法参数,在接收到传递参数时候初始化
- 在方法参数上可以使用final修饰
- 也是初始化以后不能再次修改
- 方法参数在调用方法传递参数值时初始化
- 在方法运行期间方法参数变量的值不能修改
- ```java
public class Demo02 {
public static void main(String[] args) {
Boo boo = new Boo();
//调用方法的同时 相当于为方法的参数赋值
//在方法执行的过程中,不能修改final修饰的参数
boo.sum(5,10);
}
}
class Boo{
public void sum(int a ,final int b){
a =10;
//b = 16; //编译错误 不能再次修改变量的值
System.out.println(a+b);
}
}- final修饰局部变量
final 修饰实例变量(类的成员属性)
在类中声明的对象属性,由于是属于每个对象实例的变量所有也称为”实例变量”
final可以修饰实例变量
- 必须直接初始化或者在构造器中初始化
- 实例变量在初始化以后不能再次改变了
```java
public class Demo03 {
public static void main(String[] args) {
Coo coo1 = new Coo();
System.out.println(coo1.a+” “+coo1.b);
Coo coo2 = new Coo(10);
System.out.println(coo2.a+” “+coo2.b);
//coo2.b = 15; 不能修饰final修饰的成员属性值
}
}
class Coo{
final int a =5;
final int b;
public Coo(){
this.b = 3;
}
public Coo(int b){//可以使用构造器对final修饰的成员属性赋值
this.b = b;
}
}
#### final 修饰方法
使用final修饰的方法不可以被重写
- final 修饰的方法不会影响方法在当前类中的使用,但是不能在子类中被重写修改
- 防止在子类中被"不经意"重写
实际工程中项目中较少使用final方法,原因是很多框架工具都会采用“动态代理”技术代理(重写)对象的功能,实现灵活的软件功能
```java
package day03;
public class Demo04 {
public static void main(String[] args) {
Doo doo = new Doo();
doo.hello();
SubDoo subDoo = new SubDoo();
subDoo.hello();
}
}
class Doo{
public final void hello(){
System.out.println("Hello World");
}
}
class SubDoo extends Doo{
//final修饰的方法可以被继承
//final 修饰的方法不可以被子类重写
// public void hello(){
}
}
final 修饰类
final 修饰的不可以被继承,无法派生子类
- 可以避免被继承和重写,避免被子类修改功能
- java的很多核心API,都是final类型,这样就保护了这些非常重要的API功能
实际开发中也并不建议使用final声明类
package day03; |
static 关键字
static 静态
静态变量
成员变量
作为类的成员,在类体中声明的变量称为成员变量,成员变量有三种:
- 实例变量:是属于每个对象的属性,每个对象中都有一份
- 静态变量:是属于类的变量,只有一份,全体对象共享同一份变量
- 常量:是不变的常数
静态变量
- 使用static修饰类的成员变量,称为静态变量
- 静态变量只有一份,是可以被全体对象共享的一份变量
- 用静态变量存储程序中只有一个就够的数据
- 静态变量和类的信息一起存储在方法区
- 静态变量是属于类的变量
- 使用static修饰类的成员变量,称为静态变量
静态变量的访问
static修饰的成员属性不推荐使用对象去访问,推荐用类去访问
package day03;
/**
* static修饰成员属性 被修饰的属性称为静态成员
* 属于整个类 类的所有对象共享这一个数据
*/
public class Demo06 {
public static void main(String[] args) {
//创建类的对象
Foo foo1 = new Foo();
Foo foo2 = new Foo();
foo1.a = 5;
foo2.a = 10;
foo1.b = 10;
foo2.b = 5;
System.out.println(foo1.b); //结果是5 因为foo2改掉了
System.out.println(Foo.b); //推荐用类.直接去访问
}
}
class Foo{
int a ;//普通的成员属性
static int b; //static 修饰的属性称为类属性或者类变量 属于整个类 只有一份
public void hello(){
//static int age; //static 不能直接修饰局部变量
}
}静态变量工作原理
java源文件经过编译得到字节码文件,每个类编译为一个class文件
当执行java程序时候,每用到一个类Java就会自动将对应的字节码加载到方法区
- 类中的静态变量此时在方法区中分配出来
- 字节码文件只加载一次
自动加载类
- 创建对象时
- 访问类的静态属性时
- 执行类中的静态方法时
- static final
java中同时使用static final关键字声明”常量”,常量用于声明不会变化的量
- 数学中的圆周率PI,自然数e,等等
在软件开发中也会将固定不变的数值声明为常量
package day03;
/**
* 常量 static final
* 常量在声明的时候就要赋值 以后不可以修改
*/
public class Demo07 {
public static void main(String[] args) {
System.out.println(Message.MSG_LOGIN_FAILED);
}
}
class Message{
//用static final 修饰的常量 名字采用全大写 如果是单词的组合 用 下划线隔开
public static final String MSG_LOGIN_FAILED = "用户名或密码错误";
public static final String MSG_LOGIN_SUCCESS = "登录成功" ;
}- 常量的细节
软件中不能改变的数据,应该都定义为常量
同时使用static final修饰,两个关键字的顺序可以调换
常量必须初始化
一般常量名都是大写字母,多个单词使用下划线隔开:MAX_VALUE
静态方法
静态方法:方法声明时候添加了static
属于类的方法,可以直接使用类名引用方法
普通方法可以访问静态成员,静态方法不能访问非静态成员
package day03; |
静态方法的细节
package day03;
/**
* 代码块 在类中直接使用{}编写的一段代码
*/
public class Demo09 {
public static void main(String[] args) {
Goo goo1 = new Goo();
Goo goo2 = new Goo();
System.out.println(goo1.name);
System.out.println(goo2.name);
}
}
class Goo{
String name;
//每一次创建对象 都会执行代码块中的内容
{
name = "Tom";
System.out.println("属性成功初始化");
}
}
static 其他用法
代码块
在类中直接使用{}编写的一段代码
静态代码块
类中使用static修饰的代码称为静态代码块
静态代码块在类加载期间执行
- java类只加载一次,所以静态代码块也只执行一次
- 因为这个特性,经常用静态代码块初始化类中的静态属性
```java
package day03;/**
静态代码块
加载类的时候(创建类的对象 或者访问类的静态成员)执行 以后不再执行
*/
public class Demo10 {
public static void main(String[] args) {
//java会在创建对象之前自动加载类
// Hoo hoo = new Hoo();
// Hoo hoo1 =new Hoo();
String name = Hoo.name;
System.out.println(name);
}
}
class Hoo{
static String name;/**
- 静态代码块只能访问静态成员和静态方法一样
*/
static {
System.out.println(“静态成员初始化成功”);
name =”Tom”;
}
- 静态代码块只能访问静态成员和静态方法一样
}
3. 静态导入
```java
package day03;
import static1.Person;
import static static1.Person.*; //静态导入一个类中的所有静态成员
/**
* 静态导入
*/
public class Demo11 {
public static void main(String[] args) {
Person.eat();
Person.sleep();
Person.eat();
//导入以后不需要加类名 就可以直接访问类中的静态成员
eat();
sleep();
talk();
}
}
package static1;
public class Person {
public static void talk(){
System.out.println("在说话");
}
public static void eat(){
System.out.println("在吃饭");
}
public static void sleep(){
System.out.println("在睡觉");
}
}
abstract抽象
抽象类
- 什么是抽象类
- 使用关键字abstract声明的类是抽象类,抽象类不能直接实例化创建对象
- 在面向对象设计时候,会利用“泛化”将子类的共同属性和方法抽取出来设计出父类,此时的父类往往是半成品类,只包含部分属性和方法,甚至属性值都没有合理初始化,如果直接创建对象并且使用有可能造成各种不理想结果,甚至是异常故障
- 抽象类不可以实例化
- 抽象类不可以实例化
- 面向对象设计时候根据子类泛化得到的半成品父类,应该定义为抽象类,这样可以限制创建半成品的对象,减少意外的错误发生
- abstract 和 final 不可以同时修饰一个类:final 关键字使得类不可被继承,而抽象类如果不能被继承则没有任何意义
- 如何使用抽象类
- 在类名前面添加abstract关键字
- 抽象类可以作为父类被子类继承,可以定义变量
- 抽象类不能直接创建对象
- 抽象类不可以实例化
抽象方法
什么是抽象方法
- 抽象方法:使用abstract关键字声明,不包含方法体
- 在利用泛化设计父类的时候,可能会出现全体子类都有相同的方法
- 但是每个具体方法的实现都不相同
- 只能将方法名抽取到父类
- 方法体留在每个子类中
- 把只有方法名的方法,定义为抽象方法
抽象方法的语法
- 使用abstract关键字声明方法,且不能有方法体
- 抽象类可以没有抽象方法, 但是包含抽象方法的类必须声明为抽象类
- 包含抽象方法的半成品,必须保证不能被实例化
- 子类继承抽象类的时候,必须重写(实现)其抽象方法,否则出现编译错误
- 可以将抽象方法看作父类对子类的行为约定,必须被子类重写实现
- 抽象类的意义
为其子类提供一个公共的类型
封装子类中的重复内容(成员变量和方法)
定义有抽象方法,子类虽然有不同的实现,但该方法的定义是一致的