值传递与引用传递
- 值传递并未在以下代码中产生效果
//值传递
public class Demo01 {
public static void main(String[] args) {
int a = 0;
System.out.println(a);
Demo01.change(a);
System.out.println(a);
}
public static void change(int a){
a = 10;
}
}
- 引用传递 形参为对象,本质为值传递
//引用传递
public class Demo02 {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.name);
Demo02.change(student);
System.out.println(student.name);
}
public static void change(Student student){
student.name = "lob";
}
}
class Student {
String name;
}
有参构造与无参构造
使用alt + insert 快捷键组合生成
super
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中
- super 和this不能同时调用构造方法
重写:需要有继承关系,子类重写父亲的方法(@Override)
-
方法名必须相同
-
参数列表必须相同
-
修饰符:范围可以扩大但不能缩小: public>protected>default>private
-
抛出的异常范围可以缩小但不能扩大:
ClassNotFoundException --> Exception(大)
多态
- 多态是方法的多态,属性没有多态
- 存在条件:继承关系,方法需要重写,父类引用指向子类对象!father f1 = new son();
属性赋值的先后顺序
- 默认初始化
- 显示初始化
- 构造器赋值
- 通过“对象.方法”或者“对象.属性”赋值
包装类
- 面试题一
//IntegerCache为Integer类的缓存类,默认缓存了-128~127的Integer值,如遇到[-128,127]范围的值需要转换为Integer时会直接从IntegerCache中获取,不用再去new。目的:提高效率
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//false
Integer m = 1;
Integer n = 1;
System.out.println(m == n);//true
Integer x = 128;
Integer y = 128;
System.out.println(x == y);//false
2.面试题二
//三目运算符比较基本数据类型,所以在编译阶段自动拆箱为 int 和 double 类型,由于三目运算符要求 表达式2 和 表达式3 类型一致,所以在编译阶段自动类型提升(即 int 自动类型转换为 double 类型),再自动装箱为Object,输出时使用多态调用重写的toString();即Double包装类的toString();
Object o1 = true ? new Integer(1) : new Double(2.0);
//默认调用toString方法
System.out.println(o1);//1.0
System.out.println(o1.toString());// 1.0
包装类与基本数据类型与String类型的相互转换
- 基本数据类型—>包装类:调用包装类的构造器
int num = 10;
Integer in1 = new Integer(num);
System.out.println(int.toString());//10
- 包装类—>基本数据类型:调用包装类的xxxVaule()
Integer in1 = new Integer(12);
int num2 = in1.intValue();
System.out.println(num2);
- 基本数据类型、包装类—>String类型:调用String重载的valueOf(Xxx xxx)
//方式一:连接运算
int num1 = 10;
String str1 = num1 + "";
//方式二:调用String重载的valueOf(Xxx xxx)
double num2 =12.3;
String str2 = String.valueOf(num2);//"12.3"
Float d1 = new Float(13.3f);
String str3 = String.valueOf(d1);
- String类型—>基本数据类型,包装类:调用包装类的parseXxx(String s)
String str1 = "123";
int num1 = Integer.parseInt(str1);
String str2 = "true";
boolean b1 = Boolean.parseBoolean(str2);
单例设计模式
1.饿汉式
//饿汉式
public class Singleton1 {
public static void main(String[] args) {
Bank bank1 = Bank.getInstance();
}
}
class Bank{
//1.私有化类的构造器
private Bank() {
}
//2.内部创建类的对象,且该对象也为静态
private static Bank instance = new Bank();
//3.提供公共的静态方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
- 懒汉式(未考虑线程安全版本)
//懒汉式,未考虑线程安全
public class Sington2 {
public static void main(String[] args) {
Order o1 = Order.getOrder();
Order o2 = Order.getOrder();
System.out.println(o1 == o2);
}
}
class Order{
private Order(){
}
private static Order order = null;
public static Order getOrder(){
if (order == null){
order = new Order();
}
return order;
}
}
饿汉式和懒汉式区别:从名字上来说,饿汉和懒汉,饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,而懒汉比较懒,只有当调用getInstance()的时候,才回去初始化这个单例。 另外从以下两点再区分以下这两种方式:
-
线程安全: 饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题, 懒汉式本身是非线程安全的,为了实现线程安全有几种写法,上面代码还未实现。
-
资源加载和性能: 饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成, 而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
执行顺序
静态代码块:用staitc声明,jvm加载类时执行,仅执行一次
构造代码块:类中直接用{}定义,每一次创建对象时执行。
执行顺序优先级:静态块/(当涉及到继承时,按照如下顺序执行:执行父类的静态代码块,并初始化父类静态成员变量;执行子类的静态代码块,并初始化子类静态成员变量)/,main(),构造块,构造方法。
Interface关键字/接口/匿名实现/代理模式
接口
接口是一种规范,接口本质上也是一个抽象类,不过只包含常量和方法。
接口的特点
- 接口用interface来声明
- 接口中可以声明成员变量,成员变量都是默认public static final修饰的。
- jdk1.7之前,接口中所有的方法都是抽象方法
- 接口中没有构造器(不能声明构造器)
注意
- 接口可以多继承接口的
- 接口只能被类实现
- 实现接口要重写接口中的所有的抽象方法
- 接口主要是用于被实现的(面向接口编程)
接口和抽象类的区别
-
接口和抽象都不能有实例
-
抽象类中可以有构造可以有普通方法和普通的成员变量,接口中只能声明静态公共常量(public static final),默认都是抽象方法
-
接口是需要被实现的,抽象类是需要被继承的
-
抽象类如果子类不想实现抽象类中的方法,那么该子类也必须是抽象类
-
接口可以多继承接口,实现类可以多实现接口,抽象类只能单继承
-
抽象类是重构的结果,接口是设计的结果
接口的4种实现方式
先解释一下匿名对象和匿名类。
匿名对象:匿名对象是没有名字的实体,也就是该实体没有对应的变量名引用,创建的匿名类的对象只能调用一次,匿名对象只在堆内存中开辟空间,而不存在栈内存的引用,而且每次创建匿名对象都是不同的对象。
匿名类: 匿名类 匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。匿名类相当于在定义类的同时再新建这个类的实例定义.
若要实现接口,先创建一个接口。
interface Usb{
//数据传输开始的方法
public void start();
//数据传输结束的方法
public void stop();
}
再创建一个类,传入UBS接口的实现类,可以体现接口的多态性(计算机可以加载打印机,U盘等等)
class Computer {
public static void transferData(USB usb) {
usb.start();
System.out.println(" ** 设备运行的过程 ** ");
usb.stop();
}
}
然后就可以实现接口了 main
先创建一个计算机
Computer computer = new Computer();
再传入想运行的设备(将数据线插入USB中)
- 接口的非匿名对象的非匿名实现类
Usb printer = new Printer();
computer.transferData(printer);//传输数据
Printer类是已经创建好的,以下:
class Printer implements USB {
public void start() {
System.out.println("打印机设备开始运作:");
}
public void stop() {
System.out.println("打印机设备停止运作!");
}
}
- 接口的匿名对象的非匿名实现类
computer.transferData(new Flash());
Flash类是已经创建好的,以下:
// 具体的设备U盘
class Flash implements USB {
public void start() {
//jdk1.8以后这里不用加@Override
System.out.println("U盘设备开始运作:");
}
public void stop() {
System.out.println("U盘设备停止运作!");
}
}
- 接口的非匿名对象的匿名实现类
USB phone = new USB() {
public void stop() {
System.out.println("电话设备停止运行!");
}
public void start() {
System.out.println("电话设备开始运行");
}
};
computer.transferData(phone);
显然,不需要单独创建Phone类
- 接口的匿名对象的匿名实现类
computer.transferData(new USB(){
public void start() {
System.out.println("电话设备开始运行");
}
public void stop() {
System.out.println("电话设备停止运行!");
}
});
显然,不需要单独创建Phone类,也不需要单独创建phone对象。
最后,匿名实现类适合什么时候用呢??比如,突然,想要运行一下电话设备,但以后再也不运行了,就用这个接口匿名对象类实现。
接口的应用之一:代理模式
-
意图:为其他对象提供一个代理以控制对这个对象的访问。
-
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
-
何时使用:想在访问一个类时做一些控制。
-
如何解决:增加中间层。在代理类中创建代理接口的对象,构造时传入被代理类(实现多态,所以接口类就行),并创建用于控制的方法。
静态代理
先创建代理接口
// 代理接口,可以实现浏览功能
interface NetWork{//网络
public void browse();//浏览
}
再创建一个被代理类
// 被代理类
class RealServer implements NetWork{
@Override
public void browse() {
System.out.println("上网浏览信息");
}
}
最后创建代理类(四个部分)
-
私有成员:接口对象
-
构造器:传入外界的接口对象,拷贝给成员对象
-
成员方法:随便(安全控制,或者进程外的访问等等)
-
最后,重写接口中的抽象方法
class ProxyServer implements NetWork{
private NetWork netWork;
public ProxyServer( NetWork netWork) {
this.netWork = netWork;
}
public void check() {
System.out.println("网络连接检查");
}
@Override
public void browse() {
check();
netWork.browse();
}
}
最后跑一下
public class ProxyTest {
public static void main(String[] args) {
NetWork net = new ProxyServer(new RealServer());
net.browse();
}
}
运行结果:网络连接检查
上网浏览信息
接口的应用之二:工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。(比如坐出租车时虽然你不知道太白南路怎么去,但告诉司机就行了,他会把你送到)
-
优点:
- 一个调用者想创建一个对象,只要知道其名称就可以了。
- 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
- 屏蔽产品的具体实现,调用者只关心产品的接口。
- 实现了创建着和调用者的分离,即创建对象的具体过程屏蔽隔离,提高程序的灵活性
-
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
-
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
/面向的对象的设计原则:ocp(开闭原则)/
分类:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
核心:实例化对象,用工厂替代new操作
工厂模式的实现
我们将创建一个 Car 接口和实现 Car 接口的实体类。然后定义工厂类 eFactory。
- 创建一个Car 接口
interface Car{
void run();
}
- 实现 Car接口的实体类
class Audi implements Car{
@Override
public void run() {
System.out.println("奥迪run");
}
}
class BYD implements Car{
@Override
public void run() {
System.out.println("BYD run");
}
}
- 定义工厂类 Factory
class Facatory{
public static Car getCar(String type) {
if("奥迪".equals(type)) {
return new Audi();
}else if("比亚迪".equals(type)) {
return new BYD();
}else {
return null;
}
}
}
最后,跑一下
public class FactoryTest {
public static void main(String[] args) {
Factory factory = new Factory();
Car audi = factory.getCar("奥迪");
audi.run();
Car byd = factory.getCar("比亚迪");
byd.run();
}
}
运行结果:奥迪run
BYD run