写在前面
Hello大家好, 我是【麟-小白】,一位软件工程专业的学生,喜好计算机知识。希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!!
如果小哥哥小姐姐们对我的文章感兴趣,请不要吝啬你们的小手,多多点赞加关注呀!❤❤❤ 爱你们!!!
目录
【往期回顾】
一文带你深入理解【Java基础】· 面向对象编程(中)③Object类和包装类的使用
一文带你深入理解【Java基础】·面向对象编程(中)②子类对象实例化和多态
一文带你深入理解【Java基础】· 面向对象编程(中)①继承、方法的重写、权限修饰符和super
一文带你深入理解【Java基础】· 面向对象编程(上)②重载、封装、构造器、this、package和import
一文带你深入理解【Java基础】· 面向对象编程(上)①面向对象的理解、类和对象、对象的创建使用和属性
【习题总结】
1. 关键字:static
1.1 static是什么
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下, 某些特定的数据在内存空间里只有一份 ,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。class Circle { private double radius; public Circle(double radius) { this.radius = radius; } public double findArea() { return Math.PI * radius * radius; } }
创建两个Circle对象:
Circle c1 = new Circle(2.0); //c1.radius=2.0 Circle c2 = new Circle(3.0); //c2.radius=3.0
- Circle类中的变量radius是一个实例变量(instance variable),它属于类的每一个对象,不能被同一个类的不同对象所共享。
- 上例中c1的radius独立于c2的radius,存储在不同的空间。c1中的radius变化不会影响c2的radius,反之亦然。
- 如果想让一个类的所有实例共享数据,就用类变量!
1.2 类属性、类方法的设计思想
- 类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
- 如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。
使用范围:
- 在Java类中,可用static修饰属性、方法、代码块、内部类
被修饰后的成员具备以下特点:
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
1.3 类变量和类方法
类变量:
类变量(类属性)由该类的所有实例共享:class Person { private int id; public static int total = 0; public Person() { total++; id = total; } public static void main(String args[]) { Person Tom = new Person(); Tom.id = 0; total = 100; // 不用创建对象就可以访问静态成员 } } public class StaticDemo { public static void main(String args[]) { Person.total = 100; // 不用创建对象就可以访问静态成员 // 访问方式:类名.类属性,类名.类方法 System.out.println(Person.total); Person c = new Person(); System.out.println(c.total); // 输出101 } }
类方法:
没有对象的实例时,可以用 类名 . 方法名 () 的形式访问由 static修饰的类方法。在static 方法内部只能访问类的 static 修饰的属性或方法,不能访问类的非 static的结构。class Person { private int id; private static int total = 0; public static int getTotalPerson() { // id++; //非法 return total; } public Person() { total++; id = total; } } public class PersonTest { public static void main(String[] args) { System.out.println("Number of total is " + Person.getTotalPerson()); // 没有创建对象也可以访问静态方法 // Number of total is 0 Person p1 = new Person(); System.out.println("Number of total is " + Person.getTotalPerson()); // Number of total is 1 } }
因为不需要实例就可以访问 static 方法,因此 static 方法内部不能有 this 。 也 不能有 super。 static 修饰的方法不能被重写class Person { private int id; private static int total = 0; public static void setTotalPerson(int total) { // this.total = total; // 非法,在static方法中不能有this,也不能有super } public Person() { total++; id = total; } } public class PersonTest { public static void main(String[] args) { Person.setTotalPerson(3); } }
1.4 单例 (Singleton)设计模式
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。”套路”
- 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
单例 (Singleton) 设计模式—— 饿汉式class Singleton { // 1.私有化构造器 private Singleton() { } // 2.内部提供一个当前类的实例 // 4.此实例也必须静态化 private static Singleton single = new Singleton(); // 3.提供公共的静态的方法,返回当前类的对象 public static Singleton getInstance() { return single; } }
单例(Singleton)设计模式——懒汉式
class Singleton { // 1.私有化构造器 private Singleton() { } // 2.内部提供一个当前类的实例 // 4.此实例也必须静态化 private static Singleton single; // 3.提供公共的静态的方法,返回当前类的对象 public static Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; } }
单例模式的优点:由于单例模式只生成一个实例, 减少了系统性能开销 ,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。单例 (Singleton) 设计模式 - 应用场景:
- 网站的计数器,一般也是单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
- Application 也是单例的典型应用
- Windows的Task Manager (任务管理器)就是很典型的单例模式
- Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
1.5 static代码演示
/* * static关键字的使用 * * 1.static:静态的 * 2.static可以用来修饰:属性、方法、代码块、内部类 * * 3.使用static修饰属性:静态变量(或类变量) * 3.1 属性,按是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量) * 实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的 * 非静态属性时,不会导致其他对象中同样的属性值的修改。 * 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致 * 其他对象调用此静态变量时,是修改过了的。 * 3.2 static修饰属性的其他说明: * ① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用 * ② 静态变量的加载要早于对象的创建。 * ③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。 * * ④ 类变量 实例变量 * 类 yes no * 对象 yes yes * * 3.3 静态属性举例:System.out; Math.PI; * * 4.使用static修饰方法:静态方法 * ① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用 * ② 静态方法 非静态方法 * 类 yes no * 对象 yes yes * ③ 静态方法中,只能调用静态的方法或属性 * 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性 * * 5. static注意点: * 5.1 在静态的方法内,不能使用this关键字、super关键字 * 5.2 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。 * * 6. 开发中,如何确定一个属性是否要声明为static的? * > 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。 * > 类中的常量也常常声明为static * * 开发中,如何确定一个方法是否要声明为static的? * > 操作静态属性的方法,通常设置为static的 * > 工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections */ public class StaticTest { public static void main(String[] args) { Chinese.nation = "中国"; Chinese c1 = new Chinese(); c1.name = "姚明"; c1.age = 40; c1.nation = "CHN"; Chinese c2 = new Chinese(); c2.name = "马龙"; c2.age = 30; c2.nation = "CHINA"; System.out.println(c1.nation);// CHINA // 编译不通过 // Chinese.name = "张继科"; c1.eat(); /* 中国人吃中餐 name :姚明,age : 40 name :姚明 nation : CHINA */ Chinese.show(); /* 我是一个中国人! CHINA */ // 编译不通过 // Chinese.eat(); // Chinese.info(); } } // 中国人 class Chinese { String name; int age; static String nation; public void eat() { System.out.println("中国人吃中餐"); // 调用非静态结构 this.info(); System.out.println("name :" + name); // 调用静态结构 walk(); System.out.println("nation : " + nation); } public static void show() { System.out.println("我是一个中国人!"); // 不能调用非静态的结构 // eat(); // name = "Tom"; // 可以调用静态的结构 System.out.println(Chinese.nation); walk(); } public void info() { System.out.println("name :" + name + ",age : " + age); } public static void walk() { } }
圆的练习:
//static关键字的应用 public class CircleTest { public static void main(String[] args) { Circle c1 = new Circle(); Circle c2 = new Circle(); Circle c3 = new Circle(3.4); System.out.println("c1的id:" + c1.getId());// c1的id:1001 System.out.println("c2的id:" + c2.getId());// c2的id:1002 System.out.println("c3的id:" + c3.getId());// c3的id:1003 System.out.println("创建的圆的个数为:" + Circle.getTotal());// 创建的圆的个数为:3 } } class Circle { private double radius; private int id;// 自动赋值 public Circle() { id = init++; total++; } public Circle(double radius) { this(); // id = init++; // total++; this.radius = radius; } private static int total;// 记录创建的圆的个数 private static int init = 1001;// static声明的属性被所有对象所共享 public double findArea() { return 3.14 * radius * radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } public int getId() { return id; } public static int getTotal() { return total; } }
单例设计模式:
/* * 单例设计模式: * 1. 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。 * * 2. 如何实现? * 饿汉式 vs 懒汉式 * * 3. 区分饿汉式 和 懒汉式 * 饿汉式: * 坏处:对象加载时间过长。 * 好处:饿汉式是线程安全的 * * 懒汉式:好处:延迟对象的创建。 * 目前的写法坏处:线程不安全。--->到多线程内容时,再修改 * * */ public class SingletonTest1 { public static void main(String[] args) { // Bank bank1 = new Bank(); // Bank bank2 = new Bank(); Bank bank1 = Bank.getInstance(); Bank bank2 = Bank.getInstance(); System.out.println(bank1 == bank2); } } // 饿汉式 class Bank { // 1.私有化类的构造器 private Bank() { } // 2.内部创建类的对象 // 4.要求此对象也必须声明为静态的 private static Bank instance = new Bank(); // 3.提供公共的静态的方法,返回类的对象 public static Bank getInstance() { return instance; } } /* * 单例模式的懒汉式实现 * */ public class SingletonTest2 { public static void main(String[] args) { Order order1 = Order.getInstance(); Order order2 = Order.getInstance(); System.out.println(order1 == order2); } } 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; } }
1.6 练习
编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。 账号要自动生成。编写主类,使用银行账户类,输入、输出 3 个储户的上述信息。考虑:哪些属性可以设计成static 属性。/* * 编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”, * 定义封装这些属性的方法。账号要自动生成。 编写主类,使用银行账户类,输入、输出3个储户的上述信息。 考虑:哪些属性可以设计成static属性。 */ public class Account { private int id; private String pwd = "000000"; private double balance; private static double interestRate; private static double minMoney = 1.0; private static int init = 1001;// 用于自动生成id使用的 public Account() { id = init++; } public Account(String pwd, double balance) { id = init++; this.pwd = pwd; this.balance = balance; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public static double getInterestRate() { return interestRate; } public static void setInterestRate(double interestRate) { Account.interestRate = interestRate; } public static double getMinMoney() { return minMoney; } public static void setMinMoney(double minMoney) { Account.minMoney = minMoney; } public int getId() { return id; } public double getBalance() { return balance; } @Override public String toString() { return "Account [id=" + id + ", pwd=" + pwd + ", balance=" + balance + "]"; } } public class AccountTest { public static void main(String[] args) { Account acct1 = new Account(); Account acct2 = new Account("qwerty", 2000); Account.setInterestRate(0.012); Account.setMinMoney(100); System.out.println(acct1); System.out.println(acct2); System.out.println(acct1.getInterestRate()); System.out.println(acct1.getMinMoney()); } }
2. 理解main方法的语法
2.1 main方法
- 由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。
- 又因为main()方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到。
/* * main()方法的使用说明: * 1. main()方法作为程序的入口 * 2. main()方法也是一个普通的静态方法 * 3. main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner) * */ public class MainTest { public static void main(String[] args) {// 入口 Main.main(new String[100]); /* args_0 args_1 args_2 args_3 args_4 args_5 args_6 args_7 args_8 args_9 args_10 args_11 args_12 args_13 args_14 args_15 args_16 args_17 args_18 args_19 args_20 args_21 args_22 args_23 args_24 args_25 args_26 args_27 args_28 args_29 args_30 args_31 args_32 args_33 args_34 args_35 args_36 args_37 args_38 args_39 args_40 args_41 args_42 args_43 args_44 args_45 args_46 args_47 args_48 args_49 args_50 args_51 args_52 args_53 args_54 args_55 args_56 args_57 args_58 args_59 args_60 args_61 args_62 args_63 args_64 args_65 args_66 args_67 args_68 args_69 args_70 args_71 args_72 args_73 args_74 args_75 args_76 args_77 args_78 args_79 args_80 args_81 args_82 args_83 args_84 args_85 args_86 args_87 args_88 args_89 args_90 args_91 args_92 args_93 args_94 args_95 args_96 args_97 args_98 args_99 */ MainTest test = new MainTest(); test.show(); } public void show() { } } class Main { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { args[i] = "args_" + i; System.out.println(args[i]); } } }
public class MainDemo { public static void main(String[] args) {// 假设输入1 2 3 for (int i = 0; i < args.length; i++) { System.out.println("*****" + args[i]); int num = Integer.parseInt(args[i]); System.out.println("#####" + num); /* *****1 #####1 *****2 #####2 *****3 #####3 */ } } }
结语
本人会持续更新文章的哦!希望大家一键三连,你们的鼓励就是作者不断更新的动力