static
static 常用来修饰类的成员:成员变量、方法、嵌套类
成员变量
- 被
static
修饰:类变量、成员变量、静态字段- 在程序中只占用一段固定的内存(存储在方法区),所有对象共享
- 可以通过实例、类访问 (一般用类名访问和修改,不建议用实例修改),但是不能通过类名访问实例变量和实例方法
- 没有被
static
修饰:实例 变量- 每个实例都有一份内存
java
package com.hxw.demo4;
public class Person {
public static int count = 0;
public int age;
}
java
package com.hxw.demo4;
public class Main {
public static void main(String[] args) {
Person.count = 20;
Person p1 = new Person();
p1.age = 20;
System.out.println(p1.count);
Person p2 = new Person();
p2.age = 30;
System.out.println(p2.count);
}
}
控制台输出
java
20
20
方法
- 被
static
修饰:类方法、静态方法- 可以通过实例、类访问
- 内部不可以使用
this
- 可以直接访问类变量、类方法
- 不可以直接访问实例变量和实例方法
- 没有被
static
修饰:实例方法- 只能通过实例访问,不可以通过类访问
- 内部可以使用
this
- 可以直接访问实例变量和实例方法
java
package com.hxw.demo4;
public class Person {
public static int count = 0;
public int age;
// 实例方法
public void run1(){
// 实例方法中可以使用this调用其他实例方法
this.run2();
//实例方法可以直接访问类变量和类方法
System.out.println(count);
test1();
System.out.println(this.age);
}
public void run2(){
System.out.println("void run2");
}
public static void test1(){
System.out.println("static void test1");
test2();
System.out.println(count);
}
public static void test2(){
System.out.println("static void test2");
}
}
java
package com.hxw.demo4;
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.age = 18;
person.run1();
Person.test1();
}
}
总结:
静态导入
使用了静态导入之后,就可以省略类名来访问静态成员(成员变量、方法、嵌套类)
静态导入的经典应用
为了避免每一次都是用Math.PI
,可以考虑使用静态导入,其他的Math方法也是一样的道理。
成员变量的初始化
- 编译器会自动为未初始化的成员变量设置初始值
- 如何手动给实例变量提供初始值
- 在声明中:定义变量的时候直接给其设置初始值
- 在构造方法中:在构造方法内部利用
this
进行初始化 - 在初始化中 :这个情况看下面代码,了解一下,
java
package com.hxw.dmeo6;
public class Person {
public static int count;
public int age = 1; //申明中
public Person(){
age = 20;// 在构造方法中,但是先执行初始化中的代码
}
//在初始化中
{
age = 18;
}
}
一般很少使用初始化代码,方法如下 公共初始化代码一般放到参数最多的构造方法中
java
package com.hxw.dmeo6;
public class Person {
public static int count;
public int age = 1; //申明中
public Person(){
this(0, 0);
}
public Person(int age){
this(age, 0);
}
public Person(int age, int no){
// 公共初始化代码一般放到参数最多的构造方法中
}
// //在初始化中
// {
// age = 18;
// }
}
- 如何手动给类变量提供初始值
- 在声明中
- 在静态初始化代码块中:当一个类初始化的时候 (也就是一个类第一次被主动使用时 ,JVM会对类进行初始化),就会执行静态初始化代码块,且只会执行一次。
- 可以有多个静态初始化块,按照在源码中出现的顺序被执行
初始化块和静态初始化块
这里实际的执行顺序和自己想的不一样,要先初始化完父类,再初始化子类
单例模式
如果一个类设计为单例模式,那么程序运行过程,这个类只能创建一个实例
- 饿汉单例模式:一开始就创建了对象
- 懒汉单例模式:等到该创建对象的时候创建对象,懒汉单例模式会有线程安全问题如果三个对象同时构建实例,那么会创建多个对象,但是最终创建的才会赋值给instance,所以总的说来还是单例模式
Rocket.class
java
package com.hxw.demo7;
/**
* 饿汉式单例模式:一上来就new了一个对象
*/
//public class Rocket {
// //私有的静态(唯一内存)实例变量
// private static Rocket instance = new Rocket();
//
// // 构造方法私有化 在Main中就不能new Rocket()
// private Rocket(){
//
// }
//
// //公共的静态方法,返回唯一的静态实例
// public static Rocket getInstance(){
// return instance;
// }
//}
/**
* 懒汉式单例模式
*/
public class Rocket {
public static Rocket instace = null;
private Rocket(){
}
public static Rocket getInstance(){
//用到的时候再new对象
if(instace == null){
instace = new Rocket();
}
return instace;
}
}
Main.class
java
package com.hxw.demo7;
public class Main {
public static void main(String[] args) {
Rocket r1 = Rocket.getInstance();
Rocket r2 = Rocket.getInstance();
Rocket r3 = Rocket.getInstance();
//r1 r2 r3 都是同一个instance
System.out.println(r1 == r2);
System.out.println(r2 == r3);
}
}
控制台输出
true
true
final和常量
- 被
final
修饰的类不能被继承 - 被
final
修饰的成员方法不能被重写 - 被
final
修饰的变量,只能进行一次赋值,而且需要初始化,如果定义的时候没有初始化,那么在构造函数中一定要初始化
常量(Content)
- 常量的写法:
public static final double PI = 3.14159265358979323846;
,一般名字全部用大写 - 如果将基本类型或字符串定义为常量,并且在编译时就能确定值,编译器就会使用常量值代替各处的常量名(类似于C语言的宏替换),这样可以减少访问内存的次数,提高代码运行的效率
嵌套类
内部类(Inner Class,非静态嵌套类)
- 嵌套类:定义在另一个类中的类
- 外部类:在嵌套类外层的类
- 顶级类:最外层的外部类
- 内部类 :没有被
static
修饰的嵌套类,非静态嵌套类。- 跟实例变量、实例方法一样,内部类与外部类的实例相关联**,必须先创建外部类实例,然后再用外部类实例创建内部类实例**。
- 内部类不能定义除了除编译时常量以外的任何
static
成员 - 内部类可以直接访问外部类中的所有成员(即使被申明为static)因为先创建了外部类实例,再创建内部类实例。
- 外部类可以直接访问内部类实例的成员变量、方法
外部类、内部类如下
java
package com.hxw.demo9;
public class OuterClass {
// 静态嵌套类
static class StaticNestedClass {
}
// 非静态嵌套类(内部类)
class InnerClass{
}
class A{
class B{
class C{
}
}
}
}
对应的内存图如下
静态嵌套类
- 静态嵌套类:被
static
修饰的嵌套类 - 静态嵌套类在行为上就是一个顶级类,只是定义的代码写在了另一个类中
- 对比一般的顶级类,静态嵌套类多了一些特殊权限
- 可以访问外部类中的成员(即使被申明为static)
什么情况使用嵌套类
情况一:如果类A只用在类C内部,可以考虑将类A嵌套到类C内部
- 封装性更好
- 程序包更加简化
- 增强可读性、维护性
情况二:如果类A需要经常访问类C的非公共成员,可以考虑将类A嵌套到类C中
- 另外也可以根据需要将类A隐藏起来,不对外暴露
情况三:如果需要经常访问非公共的实例成员(而且需要现有外层,后有内层的关系,例如现有公司Company
后有员工Employee
),设计成内部类(非静态嵌套类),否则设计成静态嵌套类
- 如果必须现有C实例,才能创建A实例,那么可以将A设计为C的内部类
情况一案例:
局部类
- 局部类:定义在代码块(包含
{}
,可以在里面写代码的块)中的类(可以定义在方法中,for循环中,if语句中) - 局部类不能定除编译时常量以外的任何
static
成员 - 局部类只能访问final或者有效final(只赋值一次,没有被第二次赋值)的局部变量
- 局部类可以直接访问外部类的所有成员
- 局部类只有定义在实例相关的代码块中,才能直接访问外部类中的实例成员(实例变量、实例方法)
由于局部类是定义在代码块中的,所以其作用域也只局限于代码块中,因此,什么时候会用到局部类呢?当一个类是在一个代码块中使用的时候
java
package com.hxw.demo10;
public class Person {
static {
class A{
}
}
{
int age = 10;
class A{
}
}
public void test(){
for (int i = 0; i < 10; i++) {
class A{
}
}
if(true){
class A{
}
}else{
class A{
}
}
{
class A{
}
}
}
}
抽象类
抽象方法(Abstract Method)
- 抽象方法:被
abstract
修饰的方法- 只有方法申明,没有方法实现
- 不能是
private
权限(因为定义抽象方法的目的就是为了让子类去实现) - 只能是实例方法,而不能是类方法
- 只能定义在抽象类,接口中
java
package com.hxw.demo11;
public abstract class Main {
public static void main(String[] args) {
}
public abstract void test();
}
抽象类
- 抽象类:被
abstract
修饰的类- 可以定义抽象方法
- 不能实例化,但是可以自定义构造方法
- 子类必须实现抽象父类中的所有抽象方法(除非子类也是一个抽象类)
- 可以向非抽象类一样定义成员变量、常量、嵌套类型、初始化块、非抽象方法等,也就是,抽象类也可以完全不定义抽象方法
常见使用场景
- 抽取子类的公共实现到抽象父类中,要求子类必须要单独实现的去定义成抽象方法
接口(Interface)
联想到日常笔记本电脑的接口,不同厂商的USB长得都是一样的,这是他们遵守相同的标准
-
API(Application Programming Interface)
- 应用编程接口,提供给开发者调用的一组功能(无需提供源码)
-
java中的接口
- 一系列方法申明的集合
- 用来定义规范、标准
抽象类(abstract )是通过子类继承来实现其抽象方法,接口(Interface)是通过其他类实现该接口来实现其抽象方法
家教案例引入接口概念
有一个小孩,需要应聘家教,应聘要求:
- 会教授编程
- 会教踢足球
涉及的人群可能有:在校的学生,在校的老师、出租车司机
首先看没有使用接口:
- 定义一个孩子类
java
package com.hxw.demo12;
public class Child {
private String name;
private Student jiajiao;
public Child(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student getJiajiao() {
return jiajiao;
}
public void setJiajiao(Student jiajiao) {
this.jiajiao = jiajiao;
}
public void study(){
jiajiao.jiaoBianCheng(this);
jiajiao.jiaoZuQiu(this);
}
}
- 定义一个学生类
java
package com.hxw.demo12;
public class Student {
public void jiaoBianCheng(Child child){
System.out.println("Student教" + child.getName() + "编程");
}
public void jiaoZuQiu(Child child){
System.out.println("Student教" + child.getName() + "足球");
}
}
- 主函数
java
package com.hxw.demo12;
public class Main {
public static void main(String[] args) {
Child child = new Child("Jack");
child.setJiajiao(new Student());
child.study();
}
}
如果希望老师教学生的话,按照这种方式就需要再创建一个Teacher类...如果教课的人身份众多,就会创建多个类,要单独修改Child类中的jiajiao属性,以及本类中的方法,这样很繁琐,但是这些类本质上都是实现了两个方法,招人的要求是满足这两个要求,但是对于这个人其他的特征,其实不用了解。此时,就可以将这两个行为定义到接口当中,各种身份的家教去实现这个接口即可。
- 创建JiaJiaoable接口,将原来Student中的方法复制过去,并改写为抽象方法。
java
package com.hxw.demo12;
public interface JiaJiaoable {
public abstract void jiaoBianCheng(Child child);
public abstract void jiaoZuQiu(Child child);
}
Student类、Teacher类分别实现JiaJiaoable接口中的方法
java
package com.hxw.demo12;
public class Student implements JiaJiaoable{
@Override
public void jiaoBianCheng(Child child) {
System.out.println("Student教" + child.getName() + "biancheng");
}
@Override
public void jiaoZuQiu(Child child) {
System.out.println("Student教" + child.getName() + "zuqiu");
}
}
java
package com.hxw.demo12;
public class Teacher implements JiaJiaoable{
@Override
public void jiaoBianCheng(Child child) {
System.out.println("Teacher教" + child.getName() + "biancheng");
}
@Override
public void jiaoZuQiu(Child child) {
System.out.println("Teacher教" + child.getName() + "zuqiu");
}
}
修改孩子类的家教属性为借口类型
java
package com.hxw.demo12;
public class Child {
private String name;
private JiaJiaoable jiajiao;
public Child(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public JiaJiaoable getJiajiao() {
return jiajiao;
}
public void setJiajiao(JiaJiaoable jiajiao) {
this.jiajiao = jiajiao;
}
public void study(){
jiajiao.jiaoBianCheng(this);
jiajiao.jiaoZuQiu(this);
}
}
调用Main方法:
java
package com.hxw.demo12;
public class Main {
public static void main(String[] args) {
Child child = new Child("Jack");
child.setJiajiao(new Student());
child.study();
child.setJiajiao(new Teacher());
child.study();
}
}
控制台输出
Student教Jackbiancheng
Student教Jackzuqiu
Teacher教Jackbiancheng
Teacher教Jackzuqiu
接口中可以定义的内容
- 可以定义:抽象方法、常量、嵌套类型,从java8开始定义
- 上述可以定义的内容都是隐式
public
的,因此可以省略public
关键字 - 从java9开始可以定义
private
方法
- 上述可以定义的内容都是隐式
- 常量可以省略
static
、final
- 接口中没有成员变量,写出来就是常量
- 抽象方法可以省略
abstract
- 不能自定义构造方法、不能定义初始化块、不能实例化
Lambda
- Lambda只能访问
finial
或者有效finial
的局部变量 - Lambda没有引入新的作用域
定义一个OuterClass
java
package com.hxw.demo13;
public class OuterClass {
private int age = 1;
public class InnerClass{
private int age = 2;
void inner(){
Testable t = v -> {
System.out.println(v);
System.out.println(age);
System.out.println(this.age);
System.out.println(InnerClass.this.age);
System.out.println(OuterClass.this.age);
};
t.test(3);
}
}
public static void main(String[] args) {
new OuterClass().new InnerClass().inner();
}
}