1.static关键字
1.1 问题引入:
当编写一个类时,其实就是在描述其属性和行为,而并没有产生实质上的对象,只能通过new关键字才会产生对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。有时候我们希望无论是否产生对象或无论产生多少对象的情况下,某些特定的数据在内存中只有一份。
1.2 static的使用
static可以修饰:属性、方法、代码块、内部类。
1.2.1使用static修饰属性:静态变量(类变量)
- 非静态变量(实例变量):我们创建了类的多个对象,每个对象都独立拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会影响到其他对象的非静态属性。
- 静态变量(类变量):我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象中静态变量的修改。
说明:静态变量随着类的加载而加载,因此可以用类名.静态变量
的方式调用,也可以用对象调用。
因此静态变量的加载早于对象的创建。
由于类只加载一次,则静态变量在内存中只会存在一份,在方法区的静态域中。
public class StaticTest {
public static void main(String[] args) {
Chinese.nation="Ch";
System.out.println(Chinese.nation);//CH
Chinese c1=new Chinese();
c1.name="aa";
c1.age=18;
c1.nation="Chinese";
System.out.println(c1.nation);//Chinese
Chinese c2=new Chinese();
c2.nation="UK";
System.out.println(c1.nation);//UK
}
}
class Chinese{
static String nation;
String name;
int age;
}
内存解析:
1.2.2 使用static修饰方法:静态方法
随着类的加载而加载,可以通过类名.方法名
调用,也可以通过对象调用。
public class StaticTest {
public static void main(String[] args) {
Chinese.nation="Ch";
Chinese.eat();
Chinese c2=new Chinese();
c2.eat();
}
}
class Chinese{
static String nation;
String name;
int age;
public static void eat(){
System.out.println("吃饭饭");
}
}
静态方法中,只能调用静态的方法或静态的属性,不能调用非静态的。(生命周期的原因,static表示类的加载,此时还没有对象,因此不能调用非静态)
非静态方法中,既能调用静态方法或属性,又能调用非静态的方法或属性。
在静态方法内不能使用this、super关键字。因为这两个关键字使用的前提是要有对象。
1.2.3 在开发中,如何确定一个属性是否要声明为static?
属性是可以被多个对象所共享,不会随着对象的不同而不同。
1.2.4 在开发中,如何确定一个方法是否要声明为static?
操作静态属性的方法通常就设置为静态的,如静态属性的get/set方法。
工具类中的方法,习惯上声明为static的。比如Math、Arrays
1.2.5 main()方法
作为一个程序的入口。
也是一个普通的静态方法
可以作为我们与控制台交互的方式。(如使用Scanner)
public static void main(String[] args){}
如何将控制台获取的数据传给形参:String[] args?
运行时:java 类名 “Tom” “jerry” “123” “true”
2.单例设计模式
**设计模式:**是在大量实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
**单例设计模式:**采取一定方式保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只能提供一个取得其对象实例的方法。所以首先必须将类的构造器访问权限设置为private,这样就不能用new在类的外部产生对象,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量。所以指向类内部产生的该类对象的变量也必须定义成静态的。
单例的饿汉式实现:
public class singlentonTest {
public static void main(String[] args) {
Bank1.getInstance();
}
}
class Bank1{
//1.私有化类的构造器
private Bank1(){}
//2.内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank1 instance=new Bank1();
//3.提供公共的静态方法,返回类的对象
public static Bank1 getInstance(){
return instance;
}
}
单例的懒汉式实现:
public class SingletonTest2 {
public static void main(String[] args) {
Order order1=Order.getInstance();
Order order2=Order.getInstance();
}
}
class Order{
//1.私有化类的构造器
private Order(){}
//2.声明当前类对象,没有初始化
//4.此对象也必须声明为static的
private static Order instance=null;
//3.声明public、static的返回当前类对象的方法
public static Order getInstance(){
if(instance==null)
instance=new Order();
return instance;
}
}
区分饿汉式和懒汉式
- 饿汉式:
坏处:对象加载事件过长
好处: 线程安全 - 懒汉式:
好处:延迟对象的创建
坏处:线程不安全
3.代码块(或初始化块)
作用:初始化类、对象
格式:staic{ }
或者{ }
分类:静态代码块,非静态代码块
3.1 静态代码块
public class BlockTest {
public static void main(String[] args) {
String desc1=Person.desc;//输出结果:static
String desc2=Person.desc;//不执行
Person p1=new Person();//输出结果:no static
Person p2=new Person();//输出结果:no static
}
}
class Person{
String name;
int age;
static String desc="person";
static{
desc="abc";
System.out.println("static");
}
{
age=1;
System.out.println("no static");
}
}
- 内部可以有输出语句。
- 随着类的加载而执行(调用)。也就是不需要通过“类.方法”的结构,就可以直接调用静态代码块。
- 只执行一次
- 可以定义多个静态代码块。如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行。
- 静态代码块的执行先于非静态代码块
- 只能调用静态结构
作用:初始化类的信息(静态属性)
3.2 非静态代码块
- 内部可以有输出语句。
- 随着对象的创建而执行(调用)。
- 每创建一个对象,非静态代码块就会执行一次
- 可以定义多个非静态代码块。如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行。
- 既可以调用静态结构,又可以调用非静态结构
作用:可以在创建对象时,对对象的属性进行初始化。
3.3 静态代码块、非静态代码块、构造器调用顺序
class Root{
static {
System.out.println("Root静态代码块");
}
{
System.out.println("Root非静态代码块");
}
public Root(){
super();
System.out.println("Root无参构造器");
}
}
class Mid extends Root{
static {
System.out.println("Mid静态代码块");
}
{
System.out.println("Mid非静态代码块");
}
public Mid(){
super();
System.out.println("Mid无参构造器");
}
public Mid(String msg){
this();
System.out.println("Mid带参构造器,其参数值:"+msg);
}
}
class Leaf extends Mid{
static {
System.out.println("Leaf静态代码块");
}
{
System.out.println("Leaf非静态代码块");
}
public Leaf(){
super("d-linlin");
System.out.println("Leaf构造器");
}
}
public class LeafTest {
public static void main(String[] args) {
new Leaf();
System.out.println("**********");
new Leaf();
}
}
执行结果:
总结:从父到子,静态先行
3.4 属性赋值执行的先后顺序
对属性可以赋值的位置:
1.默认初始化
2.显式初始化
3.构造器初始化
4.有了对象后,可以通过“对象.属性”或“对象.方法”的方式进行赋值
5.在代码块中赋值
①–>②/⑤–>③–>④
2和5的顺序是按照编写代码的顺序执行
4.final关键字
final:最终的、最后的
final可以修饰类、方法、变量
4.1 final修饰类
final class FinalA{}
被final修饰的类就不能被其他类继承
如:String类、System类、StringBuffer类
4.2 final修饰方法
public final void eat(){}
被final修饰的方法不能被重写
public final native Class<?>getClass();
native表示接下来调用底层的c或c++代码
4.3 final修饰变量
此时的“变量”就是一个常量了
4.3.1 final修饰属性
可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
final class FinalA{
final int width=1;//显式初始化
final int left;
final int right;
{
left=1;//代码块赋值
}
public FinalA(){
right=1;//构造器中赋值
}
public FinalA(int n){
right=n;
}
}
如果在构造器中赋值,且有多个构造器,必须在每个构造器中都给final变量赋值。一旦有某个构造器没有给其赋值,就会认为final变量没有赋值,就会报错。
不能通过函数给final属性赋值。原因:当调用构造器时,表示一个对象就创建好了,此时在内存中也就加载了属性,而final属性此时没有被赋值,因为还没有调用函数,因此不能通过函数给final属性赋值。
4.3.2 final修饰局部变量
public class FinalTEst {
public static void main(String[] args) {
FinalA a=new FinalA();
a.show(2);
a.show(1);
}
}
final class FinalA{
public void show(){
final int num=10;
num++;//错误
}
public void show(final int num){
num+=5;//错误
System.out.println(num);
}
}
尤其使用final修饰形参时,表明此形参是一个常量,当我们调用此方法时,给常量形参赋一个实参,一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
4.4 static final
用来修饰属性:全局常量
用来修饰
5.abstract关键字
可以修饰类和方法
abstract class Person{
String name;
int age;
public Person(){};
public Person(String name,int age){
this.name=name;
this.age=age;
}
public void eat(){
System.out.println("吃饭");
}
//抽象方法
public abstract void walk();
}
class Man extends Person{
public void walk(){}
}
5.1 抽象类
抽象父类不能创建对象,即不能实例化。
抽象父类一定有构造器,虽然抽象父类自己不能调用,但是子类一定会调用。
5.2 抽象方法
- 抽象方法只有方法声明,没有方法体,所以抽象方法不应该被对象调用,也就是此类不能创建对象,因此包含抽象方法的类必须是抽象类。但是抽象类可以没有抽象方法。
- 若子类不是抽象类,则子类必须重写父类(包括所有间接父类)所有的抽象方法。
- 若子类是抽象类,可以不用重写父类所有抽象方法。
5.3 abstract注意点
abstract不能修饰属性、构造器、私有方法、静态方法、final的方法、final的类
6.接口
通过接口可得到多重继承的效果。
一个类可以实现多个接口。
6.1接口的使用
①接口用interface来定义。
②如何定义接口:定义接口中的成员。
- 在JDK7之前,只能定义全局常量(public static final的,但是书写时可以省略)和抽象方法(public abstract的,也可省略不写)。
- JDK8:除了定义全局常量和抽象方法,还可以定义静态方法、默认方法。
③接口中不能定义构造器!意味着接口不能实例化(创建对象)
④Java开发中,接口通过让类去实现(implements)的方式来使用
⑤如果实现类覆盖了接口中所有抽象方法,则此实现类就可以实例化。
但如果实现类没有覆盖接口中所有抽象方法,则此实现类仍为一个抽象类实例化。
⑥Java类可以实现多个接口—>弥补了只能单继承的缺点
⑦接口与接口之间可以继承,且可以多继承。
⑧接口的使用体现了多态性。
⑨接口实际上可以看做是一种规范。
定义全局变量:
public class Inter {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
System.out.println(Flyable.MIN_SPEED);
}
}
interface Flyable{
public static final int MAX_SPEED =7900;
int MIN_SPEED =1;//省略了public static final
}
定义抽象方法:
public class Inter {
public static void main(String[] args) {
}
}
interface Flyable{
public abstract void fly();
void stop();//省略了public abstract
}
一个类实例化一个接口:
public class Inter {
public static void main(String[] args) {
}
}
interface Flyable{
public abstract void fly();
void stop();//省略了public abstract
}
class Plane implements Flyable{
@Override
public void fly() {
System.out.println("起飞");
}
@Override
public void stop() {
System.out.println("停机 ");
}
}
一个类实例化多个接口
interface Flyable{
public abstract void fly();
void stop();//省略了public abstract
}
interface Attackable{
void attack();
}
class bullet implements Attackable,Flyable{
@Override
public void fly() {
System.out.println("射击");
}
@Override
public void stop() {
System.out.println("停止");
}
@Override
public void attack() {
System.out.println("攻击");
}
}
一个子类继承父类,且实例化多个接口
格式:class B extends A implements C,D{}
interface Flyable{
public abstract void fly();
void stop();//省略了public abstract
}
interface Attackable{
void attack();
}
class bullet extends Object implements Attackable,Flyable{
@Override
public void fly() {
System.out.println("射击");
}
@Override
public void stop() {
System.out.println("停止");
}
@Override
public void attack() {
System.out.println("攻击");
}
}
接口间继承,且子类实现多个接口,包括此继承后的接口
public class Inter {
public static void main(String[] args) {
}
}
interface Flyable{
public abstract void fly();
void stop();//省略了public abstract
}
interface Attackable{
void attack();
}
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{}
class bullet extends Object implements Attackable,Flyable,CC{
@Override
public void fly() {
System.out.println("射击");
}
@Override
public void stop() {
System.out.println("停止");
}
@Override
public void attack() {
System.out.println("攻击");
}
@Override
public void method1() {}
@Override
public void method2() {}
}
如何调用接口的方法(接口的多态性)
public class Inter {
public static void main(String[] args) {
Computer com=new Computer();
Flash f=new Flash();
com.transferData(f);
}
}
class Computer{
public void transferData(USB usb){
usb.start();
usb.stop();
}
}
interface USB{
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开启");
}
@Override
public void stop() {
System.out.println("U盘停止");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开始工作");
}
@Override
public void stop() {
System.out.println("打印机停止工作");
}
}
6.2接口的应用:代理模式
package cn.demo.day.practice;
/**
* @description:
* @author: d-linlin
* @time: 2021/11/17
*/
public class NetWorkTest {
public static void main(String[] args) {
Server server=new Server();
ProxyServer proxyServer=new ProxyServer(server);
proxyServer.browse();
}
}
interface NetWork{
public void browse();
}
//被代理类
class Server implements NetWork{
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork{
private NetWork work;
public ProxyServer(NetWork work){
this.work=work;
}
public void check(){
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check();
work.browse();
}
}
6.3接口练习
package cn.demo.day.practice;
/**
* @description:
* @author: d-linlin
* @time: 2021/11/17
*/
public class InterfaceTest {
public static void main(String[] args) {
ComparableCircle c1=new ComparableCircle(3.1);
ComparableCircle c2=new ComparableCircle(3.2);
int compareValue=c1.compareTo(c2);
if(compareValue==0){
System.out.println("c1,c2一样大");
}else if(compareValue<0){
System.out.println("c2大");
}else{
System.out.println("c1大");
}
}
}
interface CompareObject{
//若返回值是0,代表相等;若为正数,代表当前对象大;负数代表当前对象小
public int compareTo(Object o);
}
class Circle{
private double redius;
public Circle(double redius){
this.redius=redius;
}
public double getRedius() {
return redius;
}
public void setRedius(double redius) {
this.redius = redius;
}
}
class ComparableCircle extends Circle implements CompareObject{
public ComparableCircle(double radius){
super(radius);
}
@Override
public int compareTo(Object o) {
if(this==o)return 0;
if(o instanceof ComparableCircle){
ComparableCircle c=(ComparableCircle) o;
//return (int)(this.getRedius()-c.getRedius());
if(this.getRedius()>c.getRedius())return 1;
else if(this.getRedius()<c.getRedius())return -1;
else{
return 0;
}
}else{
//return 0;
throw new RuntimeException("传入的数据类型不匹配");
}
}
}
6.4 Java8中接口的新特性
除了定义全局常量和抽象方法外,还可以定义静态方法(public static)、默认方法(public default,public可省略)
接口中定义的静态方法,只能通过接口去调用。
接口中的默认方法,可以通过实现类的对象去调用。
如果实现类重写了接口中的默认方法,仍然调用重写后的方法。