Углубленный анализ конечного ключевого слова

Java задняя часть JVM Безопасность

окончательный атрибут ключевого слова

Ключевое слово final широко используется в java и может объявлять переменные-члены, методы, классы и локальные переменные. Как только ссылка объявлена ​​окончательной, она не может быть изменена. Ключевое слово final также может обеспечить синхронизацию памяти.В этом блоге будут описаны характеристики ключевого слова final для обеспечения синхронизации на уровне памяти Java. Этот контент также может появляться в интервью.

конечное использование

конечная переменная

Final переменные включают переменные-члены или локальные переменные (локальные переменные внутри методов).В членах класса final часто используется вместе со static и используется как константы класса.Среди них константы класса должны быть инициализированы во время объявления, а конечные константы-члены могут быть инициализированы в конструкторе.

public class Main {
    public static final int i; //报错,必须初始化 因为常量在常量池中就存在了,调用时不需要类的初始化,所以必须在声明时初始化
    public static final int j;
    Main() {
        i = 2;
        j = 3;
    }
}

Как упоминалось выше, для констант класса JVM будет кэшировать в пуле констант и не будет загружать класс при чтении переменной.


public class Main {
    public static final int i = 2;
    Main() {
        System.out.println("调用构造函数"); // 该方法不会调用
    }
    public static void main(String[] args) {
        System.out.println(Main.i);
    }
}

окончательный метод

Окончательный метод означает, что метод не может быть переопределен методом подкласса, и метод объявлен как окончательный, который уже статически связан во время компиляции и не нуждается в динамическом связывании во время выполнения. Директива invokespecial используется при вызове последнего метода.

class PersonalLoan{
    public final String getName(){
        return"personal loan”;
    }
}
 
class CheapPersonalLoan extends PersonalLoan{
    @Override
    public final String getName(){
        return"cheap personal loan";//编译错误,无法被重载
    }
    
    public String test() {
        return getName(); //可以调用,因为是public方法
    }
}

последний класс

Конечные классы не могут быть унаследованы, а методы в конечных классах также имеют тип final по умолчанию. Классы String и Integer в java являются конечными типами.

final class PersonalLoan{}
 
class CheapPersonalLoan extends PersonalLoan {  //编译错误,无法被继承 
}

Очки знаний конечного ключевого слова

  1. Конечные переменные-члены должны быть инициализированы во время объявления или в конструкторе, иначе будет сообщено об ошибке компиляции. После инициализации конечной переменной ее нельзя переназначить.
  2. Локальным переменным должно быть присвоено значение при их объявлении. Потому что нет процесса инициализации
  3. Все переменные в анонимном классе должны быть окончательными.
  4. окончательные методы не могут быть переопределены, окончательные классы не могут быть унаследованы
  5. Все переменные, объявленные в интерфейсе, сами являются окончательными. похоже на анонимные классы
  6. Два ключевых слова final и abstract обратно пропорциональны, поэтому класс final не может быть абстрактным.
  7. Окончательные методы связываются во время компиляции, что называется статическим связыванием.
  8. Объявление классов, методов и переменных final может повысить производительность, чтобы у JVM была возможность оценить, а затем оптимизировать.

Преимущества финальных методов:

  1. Улучшена производительность, JVM кэширует конечные переменные в пуле констант.
  2. final переменные одновременно безопасны в нескольких потоках без дополнительных затрат на синхронизацию
  3. final методы компилируются статически, что повышает скорость вызова
  4. Объекты, созданные конечными классами, доступны только для чтения и могут безопасно использоваться несколькими потоками.

Понимание конечного ключевого слова из модели памяти Java

Модель памяти Java подчиняется следующим двум правилам порядка повторения для конечных полей.

  1. Начальное чтение ссылки на объект, содержащий конечное поле, и последующая начальная запись конечного поля не могут быть переупорядочены.
  2. Запись в конечное поле в конструкторе, а затем присвоение ссылки конструктора ссылочной переменной, операции не могут быть переупорядочены.

Приведенные выше два правила ограничивают инициализацию конечных полей в конструкторе и не могут быть переупорядочены вне конструктора, в отличие от обычных переменных.

Конкретная операция – это

  1. Модель памяти Java вставляет барьер памяти StoreStore перед записью конечного поля и возвратом конструктора, а статический обработчик переупорядочивает конечное поле из конструктора.
  2. Модель памяти Java вставляет барьер памяти LoadLoad между объектом, который первоначально считывает конечное поле, и последним полем в читаемом объекте.

Новый объект имеет как минимум следующие 3 шага

  1. Выделить блок памяти в куче
  2. объект для инициализации
  3. Присвоение ссылки на область памяти ссылочной переменной можно понимать как вызов специальной инструкции.

Обычные переменные-члены могут быть переупорядочены на 1-3-2 при инициализации, то есть они переупорядочены вне конструктора. конечные переменные должны быть 1-2-3 при инициализации.

Чтение и запись правил окончательного порядка повторения поля

public class FinalExample {
    int i;               
    final int j;
    static FinalExample obj;

    public void FinalExample () {
        i = 1;                   // 1
        j = 2;                   // 2
    }

    public static void writer () {  //写线程A  
        obj = new FinalExample ();  // 3
    }

    public static void reader () {       //读线程B执行
        if(obj != null) {               //4
            int a = object.i;           //5
            int b = object.j;           //6
        }
    }
}

Мы можем использовать hash-before для анализа видимости. Результат должен гарантировать, что значение, считанное a, может быть 0 или 1, а значение, считанное b, должно быть 2. Во-первых,3HB2 определяется правилами окончательной последовательности повторов., но между 3 и 1 нет отношения HB по причинам, упомянутым выше. Поскольку поток B выполняется после потока A, 3HB4. Итак, как определить отношение HB между 2 и 4?В правиле окончательного порядка повторения указано, что окончательное присваивание должно быть до возврата конструктора.. Итак, 2HB4. Потому что в одном потоке 4HB6.Так что можно сделать вывод, что 2HB5. Тогда b должен иметь возможность получить последнее значение j. И это не обязательно, потому что отношения HB нет, вы можете прочитать любое значение.

HB так удобно судить об отношениях видимости. Вы можете обратиться к другому моему блогу http://medesqure.top/2018/08/25/happen-before/

Возможные сроки выполнения показаны ниже.

конечный объект является ссылочным типом

Если конечное поле является ссылочным типом, например ссылкой на массив типа int. Для ссылочных типов правила порядка повторения записи конечных полей добавляют следующие ограничения.

  1. Между записью в поле конечного объекта, на который ссылаются, внутри конструктора и последующим присвоением ссылки на созданный объект ссылочной переменной вне конструктора нет переупорядочения. То есть сначала записать содержимое массива int[], а потом кидать ссылку.
public class FinalReferenceExample {
    final int[] intArray;                     //final是引用类型
    static FinalReferenceExample obj;
    
    public FinalReferenceExample () {        //构造函数  在构造函数中不能被重排序 final类型在声明或者在构造函数中要赋值。
        intArray = new int[1];              //1
        intArray[0] = 1;                   //2
    }
    
    public static void writerOne () {          //写线程A执行
        obj = new FinalReferenceExample ();  //3
    }
    
    public static void writerTwo () {          //写线程B执行
        obj.intArray[0] = 2;                 //4
    }
    
    public static void reader () {              //读线程C执行
        if (obj != null) {                    //5
            int temp1 = obj.intArray[0];       //6
        }
    }
}

JMM гарантирует заказ от 3 до 2. Его также можно проанализировать с использованием принципа HB, который здесь анализироваться не будет. Последовательность выполнения следующая.

6DBA7734-EFF8-4AC2-8E3B-E1645889A109

окончательные ссылки не могут «убежать» от конструкторов

Правила повторного упорядочения JMM для полей final гарантируют, что поля final правильно инициализируются в конструкторе, когда их можно безопасно читать.Но если ссылка на конструктор видна другим потокам внутри конструктора, то долгоживущая ссылка на объект исчезает в конструкторе, и окончательная видимость не гарантируется. На самом деле, это очень просто понять,Это наблюдение за инструкциями другого потока с точки зрения других потоков, что на самом деле представляет собой последовательность повторов.

public class FinalReferenceEscapeExample {
    final int i;
    static FinalReferenceEscapeExample obj;
    
    public FinalReferenceEscapeExample () {
        i = 1;       //1写final域
        obj = this;  //2 this引用在此“逸出”  因为obj不是final类型的,所以不用遵守可见性  }
    
    public static void writer() {
        new FinalReferenceEscapeExample ();
    }

    public static void reader {
        if (obj != null) {                     //3
            int temp = obj.i;                 //4
        }
    }
}

Операция 1 и операция 2 могут быть изменены. Доступ к неинициализированной переменной i осуществляется, когда за ней наблюдают другие потоки, и возможный порядок выполнения показан на рисунке.

AAF34760-7112-463C-852F-25CB775AFD62

Эта статья закончена, добро пожаловать в чтение. мой блогmedesqure.top/добро пожаловать смотреть