零基础学习java------day8------javabean编写规范,继承,static关键字,代码块,单例设计模式
0. 今日内容提要
1. javabean书写规范
javabean:一个普通的类,用来描述事物的类,里面不包含任何的业务逻辑,只是用来存储数据。
比如:Teacher,Student,Mobile....(作为数据的载体)
vo,pojo,entity,model,dto。。。。
规范:
成员变量私有化
提供get和set方法
提供无参构造方法
提供有参构造方法
自动生成get和set方法:
右键-->source-->generate getters and setters-->select All-->OK
自动生成构造方法:
右键-->source-->generate constructor using fields-->selectALL/deSelectAll-->OK
案例:
编写javabean:
商品编号
商品名称
商品价格
商品数量
public class Product { /** * 商品编号 */ private int proId; /** * 商品名称 */ private String proName; /** * 商品价格 */ private double proPrice; /** * 商品数量 */ private int proCount; public int getProId() { return proId; } public void setProId(int proId) { this.proId = proId; } public String getProName() { return proName; } public void setProName(String proName) { this.proName = proName; } public double getProPrice() { return proPrice; } public void setProPrice(double proPrice) { this.proPrice = proPrice; } public int getProCount() { return proCount; } public void setProCount(int proCount) { this.proCount = proCount; } public Product() { } public Product(int proId, String proName, double proPrice, int proCount) { super(); this.proId = proId; this.proName = proName; this.proPrice = proPrice; this.proCount = proCount; } public static void main(String[] args) { Product d1 = new Product(); d1.proId = 1; d1.proName="辣条"; d1.proPrice = 3; d1.proCount = 100; Product d2 = new Product(); d2.setProId(2); d2.setProName("火腿肠"); d2.setProPrice(1); d2.setProCount(400); Product d3 = new Product(3,"老干妈",6.5,300); } }
2. 同名局部变量和成员变量的调用问题
public class Person { String name = "张三"; // 方法一 public void print() { System.out.println(name); } // 方法二 public void print(String name) { System.out.println(name); } // 方法三 public void print1(String name) { System.out.println(this.name); } public static void main(String[] args) { Person p = new Person(); System.out.println(p.name);//张三,因为成员变量有初始值,就是张三 p.name = "李四"; p.print();//李四,因为成员变量已经被改成了李四 p.print("王五");// 王五,因为就近原则,默认使用局部变量 p.print1("赵六");// 李四,使用this调用的还是成员变量,成员变量是李四 } }
3. static 关键字
静态的方法中只能调用外部用static修饰的变量和方法,如果非要调用非静态的,需要创建对象。原因是静态的变量或方法,最先加载到内存中,而成员变量或是成员方法只能在创建新对象后才会在堆中产生。如果此时没有对象,那么成员变量或成员方法还不存在,所以不能调用。
静态变量和成员变量到底有什么区别?
静态变量只有一个,其被所有对象共享,而成员变量则不一样,每个对象都会有自己的成员变量,如下图,黄色是静态变量,车把等就是成员变量对象o和对象o1都有自己的成员变量
简述static关键字
static可以用来修饰变量和方法,被static修饰的变量和方法就变成了静态变量和静态方法。随着类的加载而加载,先于对象的存在,被所有的对象所共享,可以使用类名词调用,也可以使用对象调用,推荐使用类名调用,静态的方法中只能调用外部用static修饰的变量和方法
成员变量:对象名调用 对象名.成员变量 如 p.name;
静态变量:类名调用 类名.静态变量 如 StaticDemo.b
成员变量:也叫实例变量 实例-----对象
静态变量: 也叫类变量
案例
public class StaticDemo { int b;//成员变量 static int a; public static void main(String[] args) { // static int c=1; 注意局部变量不能用static来修饰 System.out.println(a); System.out.println(b);//此处会报错,因为b是成员变量,而此打印操作是在静态方法中进行 } }
运行结果如下图
如果非要在静态方法中调用非静态的事务,只能创建对象,如下
public class StaticDemo { int b;//成员变量 static int a; public static void main(String[] args) { System.out.println(a); StaticDemo sd = new StaticDemo(); System.out.println(sd.b); } }
4. 代码块
(1)静态代码块:
static{ } 随着类的加载而执行一次
public class CodeBlockDemo { static { System.out.println("我是静态代码块"); } public static void main(String[] args) { } } // 运行结果: 我是静态代码块
原因:main方法一启动,类就要加载,类一加载,静态代码块就会执行
(2)构造代码块
{ } 每创建一个对象就会执行一次
注意:静态代码块先于构造代码块,先于构造方法执行
(3)局部代码块:
定义在方法中的{ } 局部代码块执行完毕后会立马释放,局部代码块很少使用,但当方法中有一部分代码很耗内存,当其执行完不释放掉的话,后面代码运行效率很低,这时就可以用局部代码块来写这些代码
public class CodeBlockDemo { static { System.out.println("我是静态代码块"); } { System.out.println("我是构造代码块"); } public CodeBlockDemo() { System.out.println("我是无参构造方法"); } public CodeBlockDemo(String a) { System.out.println("我是有参构造方法"); } public static void main(String[] args) { CodeBlockDemo c1 = new CodeBlockDemo();//当没创建此对象时,运行的结果只有 “我是代码块”,说明构造方法和构造代码块只能是创建了对象才会产生 } }
运行结果: 我是代码块 我是构造方法 我是无参构造方法
由下面的代码可看出静态代码块先于构造代码块,先于构造方法执行
public class CodeBlockDemo { public CodeBlockDemo() { System.out.println("我是无参构造方法"); } public CodeBlockDemo(String a) { System.out.println("我是有参构造方法"); } { System.out.println("我是构造代码块"); } static { System.out.println("我是静态代码块"); } public static void main(String[] args) { CodeBlockDemo c = new CodeBlockDemo(); CodeBlockDemo c1 = new CodeBlockDemo("a"); } }
运行结果(并没按照代码顺序进行打印)
练习
统计一个类中创建对象的个数
此处利用构造代码块来做,构造方法也行,但没构造代码块合适,因为无论是有参对象还是无参对象构造代码块都会执行
public class CountObject { static int count = 0; { count ++; } public static void main(String[] args) { CountObject c1 = new CountObject(); CountObject c2 = new CountObject(); CountObject c3 = new CountObject(); // CountObject c4 = new CountObject(); System.out.println("创建对象的个数为"+ count);//3 } }
5. 继承
(1)格式:
class 子类名 extends 父类名{ }
被继承的类叫做父类,基类或超类;继承的类叫子类或派生类
(2)注意事项
java中的继承只支持单继承,不支持多继承(即一个子类只能有一个父类),但其支持多层继承
object:(万类之祖)如果一个类没有继承任何类,那么它默认继承自Object
父类中的私有变量或方法不能被继承
案例
1 public class ExtendsDemo { 2 public static void main(String[] args) { 3 Student s = new Student(); 4 System.out.println(s.age); 5 System.out.println(s.height); //此行代码可正常运行,表示java中的继承可以多层继承 6 s.eat(); 7 8 } 9 } 10 class SuperPerson { 11 double height; 12 } 13 class Person extends SuperPerson{ 14 String name; 15 int age; 16 char gender; 17 public void eat() { 18 System.out.println("吃嘛嘛香"); 19 } 20 } 21 class Student extends Person{ 22 double score;
(3)同一个文件中定义多个类
一个文件中可以定义多个类,但必须只能有一个类使用public类,并且要用public修饰的类必须和文件名相同,此外main方法也必须定义在用public修饰的类中(定义在非public修饰的类中无法执行)
同一个包中不能定义名字相同的类
练习
定义一个Dog
包含name gender furColor lookDoor()
定义Dog的子类ChinaDog:
眼睛的颜色eyeColor
定义Dog的子类:UsaDog
weight; age
创建一个ChinaDog对象,并赋值;创建一个UsaDog,并赋值
1 public class DogTest { 2 public static void main(String[] args) { 3 ChinaDog cd1 = new ChinaDog(); 4 cd1.eyescolor = "黑色"; 5 cd1.furColor = "黄色"; 6 cd1.gender = '公'; 7 cd1.name = "小黄"; 8 System.out.println(cd1.name+cd1.gender+cd1.furColor+cd1.eyescolor); 9 } 10 } 11 12 class Dog { 13 String name; 14 char gender; 15 String furColor; 16 public void lookDoor() { 17 System.out.println("看门"); 18 } 19 } 20 class ChinaDog extends Dog{ 21 String eyescolor; 22 } 23 24 class UsaDog extends Dog{ 25 double weight; 26 int age; 27 }
5.4 继承中成员变量的关系
当调用某子类变量时,先在子类中找,再去父类中找,父类中没有就报错
5.5 super关键字
super:可以看做是父类中的一个引用(指代父类中的对象),用与区分子类中和父类中同名的成员(成员变量,成员方法),super指代的是父类中的值,只能在之类中使用
this:可以看做是本类中的一个对象,用于区分同名的局部变量和成员变量,this指代的是成员变量,只能在本类中使用
this和super后面可以跟成员变量和成员方法(不能是局部变量)
this.成员变量 this.成员方法()
super.成员变量 this.成员方法()
调用构造方法:
this(参数):调用本类中的构造方法
super(): 调用父类中的构造方法
this和super不能用于static方法中
原因:静态方法先于对象产生,而this和super指代对象,在静态方法中,对象都还没产生,所以this和static不能用于static方法中
5.6 继承中构造方法的关系
子类中的构造方法会默认调用父类中无参数的构造方法,如果父类中没有无参数的构造方法,子类必须直接或简介的调用父类中的构造方法
如下代码子类会默认调用父类构造方法
1 //创建测试类 2 public class ExtendsConstrutorDemo { 3 public static void main(String[] args) { 4 SonClass s1 = new SonClass(); 5 } 6 } 7 // 创建一个父类 8 class FatherClass { 9 public FatherClass() { 10 System.out.println("我是父类无参构造方法"); 11 } 12 } 13 // 创建子类 14 class SonClass extends FatherClass{ 15 public SonClass() {
16 // 此处默认会有super(),用来调用父类构造方法,只能放第一行 17 System.out.println("我是子类无参构造方法"); 18 } 19 }
打印结果为
若第9行的代码中,父类构造方法加参数,变成如下
public FatherClass(String str){
这样的话,子类中调用父类构造方法处就会报错(提示无父类构造方法,即super()调用不到父类中的构造方法)
解决办法:
第一种:直接调用:
在super()中加一个参数即可,如super(“str”)
第二种:间接调用
在本类中再写一个有参的构造方法,利用this调用这个有参的构造方法,部分代码如下图
调用本类中的构造方法:
this(参数值);此也一定要放到第一行,和super()一样
为什么要调用父类的构造方法?
子类要继承父类中的数据,父类中有时候会有些成员变量和成员方法,而要调用父类中的数据,父类就要将这些数据初始化(即将这些数据创建好),而调用父类造方法,就能达到父类数据初始化的效果。
练习
/** * 构建一个父类,两个子类 * 1.构造两个父类中和子类中名字一样的属性和方法,属性赋不同的值 * 分别用父类和子类的对象调用 * 2.为父类和子类提供无参构造方法,并显示调用super(); * 3.为子类提供有参构造方法1,并显示调用本类中的无参构造方法this(); * 4.为子类提供有参构造方法2,并显示调用父类中的构造方法 * 5.为父类提供有参数构造方法,在子类中构造方法中调用 */ package com._51doit.javase.day08; public class ExtendsTest1 { public static void main(String[] args) { FuClass f1 = new FuClass(); System.out.println(f1.name);// NvClass n1 = new NvClass(4); n1.sleep(); } } class FuClass{ String name = "老张"; public FuClass() { super();//调用Object中的构造方法 } public void sleep() { System.out.println("生前何须久睡死后自会长眠"); } public FuClass(int a) { } } class ZiClass extends FuClass{ public ZiClass() { super(); } public ZiClass(int a) {//有参 this();//调用本类中无参构造方法 } String name = "大张"; public void sleep() { System.out.println("中午不睡下午崩溃"); } } class NvClass extends FuClass{ String name = "小张"; public NvClass(int b) { super(1);//调用父类中有参的构造方法 } /*public void sleep() { System.out.println("别睡太晚,梦会太短"); }*/ }
5.7 继承中成员方法的关系
与成员变量相似,先在子类中找,再去父类中找,父类中没有就报错
5.8 方法的重载和方法的重写
概念:
重写(override):子类中出现了和父类中方法名一样,返回值类型,参数列表一样的方法,就叫做方法的重写
重载(overload):一个类中可以存在多个名字相同的方法,但是必须保证参数的个数或类型不同,与返回值无关
注解:@override 可以使用该注解验证重写格式是否正确,把它写到子类的方法上
如:
方法重写的注意事项
1. 父类中的私有方法不能被重写(子类中可以有同名的方法,只是此种情况不叫方法的重写)
2. 子类重写父类中的方法,访问权限不能更低(权限一次减小:public default protected private)
3. 静态的方法不能重写
练习题
按要求编写一个Java 应用程序:
(1)编写一个矩形类Rect,包含:
矩形的宽width;矩形的高height。
两个构造方法:
1.一个带有两个参数的构造方法,用于将width 和height 属性初化;
2.一个不带参数的构造方法,将矩形初始化为宽和高都为10。
两个方法:
求矩形面积的方法area()
求矩形周长的方法perimeter()
(2)通过继承Rect 类编写一个具有确定位置的矩形类PlainRect,其确定位置用
矩形的左上角坐标来标识,包含:
添加两个属性:矩形左上角坐标startX 和startY。
两个构造方法:
带4 个参数的构造方法,用于对startX、startY、width 和height 属性
初始化;
不带参数的构造方法,将矩形初始化为左上角坐标、长和宽都为0
的矩形;
添加一个方法:
判断某个点是否在矩形内部的方法isInside(double x,double y)。如在矩
形内,返回true, 否则,返回false。
提示:点在矩形类是指满足条件:
x>=startX&&x<=(startX+width)&&y<=startY&&y>=(startY-height)
(3)编写PlainRect 类的测试程序
创建一个左上角坐标为(10,10),长为20,宽为10 的矩形对象;
计算并打印输出矩形的面积和周长;
判断点(25.5,13)是否在矩形内,并打印输出相关信息。
6. final
final 可以用来修饰类,方法,变量
final 修饰的类不能被继承
final 修饰的方法不能被重写
final 修饰的变量值不能改变,final修饰的变量要有初始值
一般我们创建常量:public static final
final: 限制值不能变
static: 只有一份
两种文法
final static
final finally finalize
7. 单例设计模式
概述:一个类只产生一个实例对象
分类:饱汉式
懒汉式
实现步骤:
1. 私有构造方法
2. 创建最终静态对象
3. 提供公共的访问对象
如何实现一个类只能创建一个对象?
正常情况创建对象的形式为:类名 对象名 = new 类的构造方法
思路:将构造方法属性变为私有,即用private修饰,这样外部类就不能通过 new 类构造方法来创建对象了。但自己可以在类的内部创建一个公有的对象(即为成员变量,加上static就变成静态变量,此处因为是各个类公有,所以为应该为静态变量),供外部类调用。
如下:
1 public class Singleton { 2 public static void main(String[] args) { 3 Singleton1 s1 = Singleton1.s;// 通过类调用静态变量s得到一个对象 4 Singleton1 s2 = Singleton1.s; 5 System.out.println(s1 == s2); 6 } 7 } 8 9 10 class Singleton1 { 11 public static Singleton1 s = new Singleton1();//此处一定要加static,让Singleton1类型的s成员变量变成静态变量,不然只能通过对象调用,不能类调用。 12 private Singleton1() { 13 } 14 }
运行结果为true,说明对象s1和对象s2是同一个对象
但这种直接获取成员变量的形式不好,应该通过创建方法的形式,如下(也称饱汉式)
public class Singleton { public static void main(String[] args) { Singleton1 s1 = Singleton1.getInstance(); Singleton1 s2 = Singleton1.getInstance(); System.out.println(s1 == s2); } } class Singleton1 { public static Singleton1 s = new Singleton1();
// 创建一个静态的访问方法,返回本类中唯一的对象 public static Singleton1 getInstance() { //此处的Singleton1表示返回Singleton1类型的值 return s; } private Singleton1() { } }
运行结果 true
懒汉式
由于懒汉式一开始就创立了一个对象,即在堆中占据了一个内存空间,若其他类一直没有去调用这个获取对象的方法,这样就很浪费资源,所以就有懒汉式的出现
public class Singleton2 { public static void main(String[] args) { Singleton3 s1 = Singleton3.getInstance(); Singleton3 s2 = Singleton3.getInstance(); System.out.println(s1); } } class Singleton3{ public static Singleton3 s; public static Singleton3 getInstance() { if(s==null) { s = new Singleton3(); } return s; } private Singleton3() { } }
懒汉式也存在问题,会有线程安全问题(创建多个对象),如:
当一个线程Thread1刚执行完if判断语句时,cpu切换至另一个线程,此线程一直执行完,创建了一个s对象,此时cpu又切换回去执行线程Thread1,此时就又会创建一个s对象,这样就会创建多个对象(如果有多个线程)
解决方法,上一把锁,使用同步方法解决(即在方法的修饰符位置加上synchronized),如下:
public synchronized static Singleton3 getInstance()
-