• JAVA基础(十二)


    关键字:static

    当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上 的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象, 其方法才可以供外部调用。

    我们有时候希望无论是否产生了对象或无论产生了多少 对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个 国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中 都单独分配一个用于代表国家名称的变量。

    使用static修饰属性:静态变量

    使用范围:

            在Java类中,可用static修饰属性、方法、代码块、内部类

    被修饰后的成员具备以下特点:

            随着类的加载而加载

            优先于对象存在

            修饰的成员,被所有对象所共享

            访问权限允许时,可不创建对象,直接被类调用

            类中的常量也常常声明为static 

            由于类只会加载一次,则静态变量在内存也只会存在一份存放在方法区的静态域中

            属性的分类:按是否使用static修饰,又分为:静态属性和非静态属性(实例变量)。

                    实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性,当修改其中一个对象的非静态属性时,不会导致其他对象中同样的属性值的修改。                                                                           

                    静态变量:我们创建了类的多个对象,多个对象共享一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过的                                       

     如果想让一个类的所有实例共享数据,就用类变量!

    静态结构使用例:

    System.out 、Math.PI

     使用static修饰方法:静态方法

            随着类的加载二加载,可以通过类.静态方法的方式进行调用

            静态方法中只能调用静态的方法或属性

            非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性

            静态方法中不能使用this和super关键字

    类属性、类方法的设计思想

    类属性作为该类各个对象之间共享的变量。在设计类时,分析哪 些属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。

    如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。

    操作静态属性的方法通常设置为静态

    工具类的方法习惯上声明为static

    测试代码

    1. package com.xxx.java;
    2. public class StaticTest {
    3. public static void main(String[] args) {
    4. Chinese c1 = new Chinese();
    5. c1.name = "姚明";
    6. c1.age = 40;
    7. Chinese c2 = new Chinese();
    8. c2.name = "马龙";
    9. c2.age = 30;
    10. c1.nation = "CHN";
    11. System.out.println(c2.nation);
    12. Chinese.show();
    13. //Chinese.eat();
    14. }
    15. }
    16. class Chinese{
    17. String name;
    18. int age;
    19. static String nation;
    20. public void eat() {
    21. System.out.println("吃中餐");
    22. }
    23. public static void show() {
    24. System.out.println("我是一个中国人");
    25. //eat();
    26. //name = "Tom";
    27. //可以调用静态的结构
    28. nation = "中国";
    29. walk();
    30. }
    31. public static void walk() {
    32. System.out.println("走路");
    33. }
    34. }

    ArrayUtil工具类优化

    1. package com.xxx.java;
    2. //自定义数组工具类
    3. public class ArrayUtil {
    4. // 求数组的最大值
    5. public static int getMax(int[] arr) {
    6. int max = arr[0];
    7. for (int i = 0; i < arr.length; i++) {
    8. if (max < arr[i]) {
    9. max = arr[i];
    10. }
    11. }
    12. return max;
    13. }
    14. // 求数组的最小值
    15. public static int getMin(int[] arr) {
    16. int min = arr[0];
    17. for (int i = 0; i < arr.length; i++) {
    18. if (min > arr[i]) {
    19. min = arr[i];
    20. }
    21. }
    22. return min;
    23. }
    24. // 求数组的总和
    25. public static int getSum(int[] arr) {
    26. int sum = 0;
    27. for (int i = 0; i < arr.length; i++) {
    28. sum += arr[i];
    29. }
    30. return sum;
    31. }
    32. // 求数组的平均值
    33. public static int getAvg(int[] arr) {
    34. return getSum(arr) / arr.length;
    35. }
    36. // 反转数组
    37. public static void reverse(int[] arr) {
    38. for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
    39. int temp = arr[i];
    40. arr[i] = arr[j];
    41. arr[j] = temp;
    42. }
    43. }
    44. // 复制数组
    45. public static int[] copy(int[] arr) {
    46. int[] arr1 = new int[arr.length];
    47. for (int i = 0; i < arr.length; i++) {
    48. arr1[i] = arr[i];
    49. }
    50. return arr1;
    51. }
    52. // 数组排序
    53. public static void sort(int[] arr) {
    54. for (int i = 0; i < arr.length; i++) {
    55. for (int j = 0; j < arr.length - 1 - i; j++) {
    56. if (arr[j] > arr[j + 1]) {
    57. swap(arr,j,j+1);
    58. }
    59. }
    60. }
    61. }
    62. // 遍历数组
    63. public static void print(int[] arr) {
    64. for (int i = 0; i < arr.length; i++) {
    65. System.out.print(arr[i] + " ");
    66. }
    67. }
    68. // 查找指定元素
    69. public static int getIndex(int[] arr, int dest) {
    70. for (int i = 0; i < arr.length; i++) {
    71. if (dest == arr[i]) {
    72. return i;
    73. }
    74. }
    75. return -1;
    76. }
    77. //交换数组中指定两个位置元素的值
    78. private static void swap(int[]arr,int i,int j) {
    79. int temp = arr[i];
    80. arr[i] = arr[j];
    81. arr[j] = temp;
    82. }
    83. }

    测试

    1. package com.xxx.java;
    2. public class ArrayUtilTest {
    3. public static void main(String[] args) {
    4. //ArrayUtil util = new ArrayUtil();
    5. int[] arr = {32,34,32,5,3,54,654,-98,0,-53,5};
    6. int max = ArrayUtil.getMax(arr);
    7. System.out.println("最大值为:" + max);
    8. // System.out.println("排序前");
    9. // ArrayUtil.print(arr);
    10. //
    11. // ArrayUtil.sort(arr);
    12. // System.out.println();
    13. // System.out.println("排序后");
    14. // ArrayUtil.print(arr);
    15. System.out.print("查找:");
    16. int index = ArrayUtil.getIndex(arr, -5);
    17. if (index >= 0) {
    18. System.out.println(index);
    19. } else {
    20. System.out.println("没找到");
    21. }
    22. }
    23. }

    相关应用

    1. package com.xxx.java;
    2. public class CircleTest {
    3. public static void main(String[] args) {
    4. Circle c1 = new Circle();
    5. Circle c2 = new Circle();
    6. Circle c3 = new Circle(3.4);
    7. System.out.println("c1的id为:" + c1.getId());
    8. System.out.println("c2的id为:" + c2.getId());
    9. System.out.println("c3的id为:" + c3.getId());
    10. System.out.println("创建的圆的个数为:" + Circle.getTotal());
    11. }
    12. }
    13. class Circle {
    14. private double radius;
    15. private int id;
    16. private static int total;
    17. private static int init = 1001;
    18. public Circle() {
    19. super();
    20. id = init++;
    21. total++;
    22. }
    23. public Circle(double radius) {
    24. this();
    25. this.radius = radius;
    26. //id = init++;
    27. //total++;
    28. }
    29. public static int getTotal() {
    30. return total;
    31. }
    32. public double getRadius() {
    33. return radius;
    34. }
    35. public void setRadius(double radius) {
    36. this.radius = radius;
    37. }
    38. public int getId() {
    39. return id;
    40. }
    41. public double findArea() {
    42. return 3.14 * radius * radius;
    43. }
    44. }

    相关练习

    编写一个类实现银行账户的概念,包含的属性有“帐号”、“密 码”、“存款余额”、“利率”、“最小余额”,定义封装这些 属性的方法。账号要自动生成。

    编写主类,使用银行账户类,输入、输出3个储户的上述信息。 考虑:哪些属性可以设计成static属性。

    1. package com.xxx.exer;
    2. public class Account {
    3. private int id;
    4. private String pwd = "000000";
    5. private double balance;
    6. private static double interestRate;
    7. private static double minMoney;
    8. private static int init;
    9. public Account() {
    10. super();
    11. id = init++;
    12. }
    13. public Account(String pwd, double balance) {
    14. this();
    15. this.pwd = pwd;
    16. this.balance = balance;
    17. }
    18. public String getPwd() {
    19. return pwd;
    20. }
    21. public void setPwd(String pwd) {
    22. this.pwd = pwd;
    23. }
    24. public static double getInterestRate() {
    25. return interestRate;
    26. }
    27. public static void setInterestRate(double interestRate) {
    28. Account.interestRate = interestRate;
    29. }
    30. public static double getMinMoney() {
    31. return minMoney;
    32. }
    33. public static void setMinMoney(double minMoney) {
    34. Account.minMoney = minMoney;
    35. }
    36. public int getId() {
    37. return id;
    38. }
    39. public double getBalance() {
    40. return balance;
    41. }
    42. @Override
    43. public String toString() {
    44. return "Account [id=" + id + ", pwd=" + pwd + ", balance=" + balance + "]";
    45. }
    46. }

    测试

    1. package com.xxx.exer;
    2. public class AccountTest {
    3. public static void main(String[] args) {
    4. Account.setInterestRate(0.012);
    5. Account.setMinMoney(100);
    6. Account acc1 = new Account();
    7. Account acc2 = new Account("114514",2000);
    8. System.out.println(acc1);
    9. System.out.println(acc2);
    10. System.out.println(Account.getInterestRate());
    11. System.out.println(Account.getMinMoney());
    12. }
    13. }

    单例 (Singleton)设计模式

    设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。”套路”

    所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对 某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构 造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生 类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无 法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象, 静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象 的变量也必须定义成静态的。

    饿汉式:

    1. package com.xxx.java1;
    2. public class SingletonTest1 {
    3. public static void main(String[] args) {
    4. Bank bank1 = Bank.getInstance();
    5. Bank bank2 = Bank.getInstance();
    6. System.out.println(bank1 = bank2);
    7. }
    8. }
    9. //饿汉式
    10. class Bank{
    11. //私有化构造器
    12. private Bank(){
    13. }
    14. //内部创建类的对象
    15. //要求声明为静态,否则静态方法中无法调用
    16. private static Bank instance = new Bank();
    17. //提供public的方法,返回类的对象
    18. public static Bank getInstance() {
    19. return instance;
    20. }
    21. }

    懒汉式:

    1. package com.xxx.java1;
    2. public class SingletonTest2 {
    3. public static void main(String[] args) {
    4. Order order1 = Order.getInstance();
    5. Order order2 = Order.getInstance();
    6. System.out.println(order1 == order2);
    7. }
    8. }
    9. //懒汉式
    10. class Order {
    11. //私有化类的构造器
    12. private Order() {
    13. }
    14. //声明当前类对象,不进行初始化
    15. private static Order instance = null;
    16. //声明public static的返回当前类对象的方法
    17. public static Order getInstance() {
    18. //不判断会每次执行时都新生成一个对象实例
    19. if(instance == null) {
    20. instance = new Order();
    21. }
    22. return instance;
    23. }
    24. }

    区分饿汉式和懒汉式:

            懒汉式:

                    优点:延迟对象的创建

                    缺点:线程不安全——到多线程时修改

            饿汉式:

                    优点:饿汉式是线程安全的

                    缺点:对象加载时间过长

    单例模式的优点:

            由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的 产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决

    应用场景

     网站的计数器,一般也是单例模式实现,否则难以同步。

     应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

     数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。

     项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。

     Application 也是单例的典型应用

     Windows的Task Manager (任务管理器)就是很典型的单例模式

     Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

    理解main方法的语法

    说明

            作为程序的入口

            是一个普通的静态方法

    public:由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public

    static:又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的

    void:main方法执行完后程序就结束了,不需要返回值

    形参:该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行               的类的参数。

    形参的使用方式(Eclipse)

    1.先run as一遍代码

    2.选择Run Configurations 

     3.左侧选择当前编译好的字节码文件,右侧选择Arguments

    在Program arguments框内输入字符串,空格隔开,可以不加双引号。Run运行程序 

    执行结果如下

     相关代码

    1. package com.xxx.java1;
    2. public class MainDemo {
    3. public static void main(String[] args) {
    4. for (int i = 0; i < args.length; i++) {
    5. System.out.println("***********" + args[i]);
    6. }
    7. }
    8. }

    若是其他基本数据类型,可以使用包装类的parseXxx方法 

    1. package com.xxx.java1;
    2. public class MainTest {
    3. public static void main(String[] args) {
    4. Main.main(new String[100]);
    5. }
    6. }
    7. class Main{
    8. public static void main(String[] args) {
    9. args = new String[100];
    10. for (int i = 0; i < args.length; i++) {
    11. args[i] = "args_" + i;
    12. System.out.println(args[i]);
    13. }
    14. }
    15. }

     形参的使用方式(控制台)

    1、先删掉包名,在编译javac xxx

    2、java xxx 形参

    代码块(或初始化块)

    代码块(或初始化块)的作用:对Java类或对象进行初始化。

    代码块(或初始化块)的分类:

            一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块 (static block),没有使用static修饰的,为非静态代码块。         

    static代码块通常用于初始化static的属性

    静态代码块:

            可以有输出语句。

            初始化类的信息。

            不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。

            若有多个静态的代码块,那么按照从上到下的顺序依次执行。

            静态代码块的执行要先于非静态代码块。

            静态代码块随着类的加载而加载,且只执行一次。

    非静态代码块:

            可以有输出语句。

            可以在创建对象时,对对象的属性进行初始化。

            除了调用非静态的结构外,还可以调用静态的变量或方法。

            若有多个非静态的代码块,那么按照从上到下的顺序依次执行。

            每次创建对象的时候,都会执行一次。且先于构造器执行。

    代码

    1. package com.xxx.java2;
    2. public class BlockTest {
    3. public static void main(String[] args) {
    4. String desc = Person.desc;
    5. System.out.println(desc);
    6. Person p1 = new Person();
    7. Person p2 = new Person();
    8. System.out.println(p1.age);
    9. Person.info();
    10. }
    11. }
    12. class Person {
    13. //属性
    14. String name;
    15. int age;
    16. static String desc = "我是一个人";
    17. //构造器
    18. public Person() {
    19. }
    20. public Person(String name,int age) {
    21. this.name = name;
    22. this.age = age;
    23. }
    24. //代码块
    25. static {
    26. System.out.println("hello,static block-1");
    27. desc = "我是一个爱学习的人";
    28. info();
    29. }
    30. static {
    31. System.out.println("hello,static block-2");
    32. }
    33. {
    34. System.out.println("hello,block-1");
    35. age = 1;
    36. }
    37. {
    38. System.out.println("hello,block-2");
    39. }
    40. //方法
    41. public void eat() {
    42. System.out.println("吃饭");
    43. }
    44. @Override
    45. public String toString() {
    46. return "Person [name=" + name + ", age=" + age + "]";
    47. }
    48. public static void info() {
    49. System.out.println("我是一个快乐的人");
    50. }
    51. }

    使用示例

            例1

    1. package com.xxx.java2;
    2. class Root{
    3. static{
    4. System.out.println("Root的静态初始化块");
    5. }
    6. {
    7. System.out.println("Root的普通初始化块");
    8. }
    9. public Root(){
    10. System.out.println("Root的无参数的构造器");
    11. }
    12. }
    13. class Mid extends Root{
    14. static{
    15. System.out.println("Mid的静态初始化块");
    16. }
    17. {
    18. System.out.println("Mid的普通初始化块");
    19. }
    20. public Mid(){
    21. System.out.println("Mid的无参数的构造器");
    22. }
    23. public Mid(String msg){
    24. //通过this调用同一类中重载的构造器
    25. this();
    26. System.out.println("Mid的带参数构造器,其参数值:"
    27. + msg);
    28. }
    29. }
    30. class Leaf extends Mid{
    31. static{
    32. System.out.println("Leaf的静态初始化块");
    33. }
    34. {
    35. System.out.println("Leaf的普通初始化块");
    36. }
    37. public Leaf(){
    38. //通过super调用父类中有一个字符串参数的构造器
    39. super("尚硅谷");
    40. System.out.println("Leaf的构造器");
    41. }
    42. }
    43. public class LeafTest{
    44. public static void main(String[] args){
    45. new Leaf();
    46. //new Leaf();
    47. }
    48. }

    解析:

    先加载父类和间接父类,然后加载每个类静态代码块即:

            加载Object的静态代码块(无)

            加载Root的静态代码块

            加载Mid的静态代码块

            加载Leaf的静态代码块

    之后对象创建完毕,先加载父类和间接父类的构造器,而此时非静态代码块先于父类构造器执行:

            加载Object的代码块(无),加载Object的构造器(无内容)

            加载Root的代码块,加载Root的构造器

            加载Mid的代码块,加载Mid的无参构造器,加载Mid的带参构造器

            加载Leaf的代码块,加载Leaf的构造器        

    若再次new Leaf(),静态代码块内容不在执行

            例2

    1. package com.xxx.java2;
    2. class Father {
    3. static {
    4. System.out.println("11111111111");
    5. }
    6. {
    7. System.out.println("22222222222");
    8. }
    9. public Father() {
    10. System.out.println("33333333333");
    11. }
    12. }
    13. public class Son extends Father {
    14. static {
    15. System.out.println("44444444444");
    16. }
    17. {
    18. System.out.println("55555555555");
    19. }
    20. public Son() {
    21. System.out.println("66666666666");
    22. }
    23. public static void main(String[] args) { // 由父及子 静态先行
    24. System.out.println("77777777777");
    25. System.out.println("************************");
    26. new Son();
    27. //System.out.println("************************");
    28. //new Son();
    29. //System.out.println("************************");
    30. //new Father();
    31. }
    32. }

    解析: 

            main方法是由当前main所在的类通过类.main调用的,所有先初始化当前类以及他的父类和间接父类             

            当前main方法在Son中,由Son.main()调用,要初始化Son就要先初始化Son的父类和间接父类         

            初始化Object

            初始化Father(静态代码块)

            初始化Son(静态代码块)

    后续执行同例1

    结果为:1 4 7 * 2 3 5 6 * 2 3 5 6 * 2 3

    对属性的赋值顺序

    1.默认初始化

    2.显式初始化 / 3在代码块中赋值

    4构造器中初始化

    5对象.属性或对象.方法的方式赋值

    执行顺序1——2 / 3——4——5

    1. package com.xxx.java2;
    2. public class OrderTest {
    3. public static void main(String[] args) {
    4. Order o = new Order();
    5. System.out.println(o.orderId);
    6. }
    7. }
    8. class Order{
    9. // int orderId = 3;
    10. // {
    11. // orderId = 4;
    12. // }
    13. {
    14. orderId = 4;
    15. }
    16. int orderId = 3;
    17. }

    关键字:native

    native关键字声明调用底层c或c++的方法

    关键字:final

    在Java中声明类、变量和方法时,可使用关键字final来修饰。

            final标记的类不能被继承。

                    内置的final类:String、System、StringBuffer

            final标记的方法不能被子类重写。

                    内置的funal方法:Object的getClass()

    final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。

            final修饰属性,可以赋值的位置有

                    显式初始化、在代码块中赋值、在构造器中赋值

            final修饰局部变量:为常量,无法修改

                          修饰形参:表示此形参是一个常量,只能在方法内使用该形参,不能对其进行修改

            static final:修饰属性 全局常量

    1. package com.xxx.java2;
    2. public class FinalTest {
    3. // final int UP; 无法默认初始化赋值
    4. final int WIDTH = 10; // 可以显式初始化赋值
    5. // 可以在代码块内赋值
    6. final int LEFT;
    7. {
    8. LEFT = 1;
    9. }
    10. // 可以在构造器中赋值,但每个构造器中都必须赋值
    11. final int RIGHT;
    12. public FinalTest() {
    13. RIGHT = 2;
    14. }
    15. public FinalTest(int n) {
    16. RIGHT = n;
    17. }
    18. //不可以在方法中赋值
    19. // final int DOWN;
    20. //
    21. // public int setDOWN(int down) {
    22. // DOWN = down;
    23. // }
    24. // public void doWidth() {
    25. // WIDTH = 20;
    26. // }
    27. public void show() {
    28. final int NUM = 10; //常量
    29. //NUM += 1;
    30. }
    31. public void show(final int num) {
    32. //num = 5;
    33. System.out.println(num);
    34. }
    35. public static void main(String[] args) {
    36. FinalTest test = new FinalTest();
    37. //test.setDOWN(5);
    38. test.show(10);
    39. }
    40. }
    41. final class FinalA {
    42. }
    43. //class B extends FinalA{
    44. //
    45. //}
    46. //class C extends String{
    47. //
    48. //}
    49. class AA {
    50. public final void name() {
    51. }
    52. }
    53. class BB extends AA {
    54. // public void name() {
    55. //
    56. // }
    57. }

    练习:

    1

    1. public class Something {
    2. public int addOne(final int x) {
    3. return ++x; //不行
    4. // return x + 1; //可以,x本身没有变
    5. }
    6. }

    2

    1. public class Something {
    2. public static void main(String[] args) {
    3. Other o = new Other();
    4. new Something().addOne(o);
    5. }
    6. public void addOne(final Other o) {
    7. // o = new Other(); //不行,地址变了
    8. o.i++; //可以,地址没有变
    9. }
    10. }
    11. class Other {
    12. public int i;
    13. }

  • 相关阅读:
    【云原生之K8S】K8S管理工具kubectl 详解
    springboot整合webSocket(看完即入门)
    http post协议发送本地压缩数据到服务器
    mysql跨库关联查询(dblink)
    数据可视化分析之新技能——魔数图
    go设计模式之工厂方法模式
    mysql_命令行启动_win10
    5.云原生-KubeSphere3.3.0安装Harbor仓库
    九、MySQL之视图的介绍
    分享如何通过股票交易行情数据接口获取日线行情
  • 原文地址:https://blog.csdn.net/m0_56413004/article/details/126146611