第三章
Java是一种强类型语言。https://blog.csdn.net/qq_36192099/article/details/79464196
在Java中,整型的范围与机器无关。
int 4字节
short 2字节
long 8字节
byte 1字节子串:Substring
拼接:String.join
String类对象是不可变字符,可通过substring+”” 改变字符串变量
不能用==来比较字符串,应该用equals。一个常量和一个变量比较,推荐把常量写在前面任何一个Java对象都可以转换成字符串
输入输出
首先要构造一个Scanner对象。
Scanner类有各种方法,nextLine()读取一行,next()读取一个单词,nextInt()读取一个整数,nextDouble()读取一个浮点数。Java提供了一种带标签的break语句。只能跳出语句块,而不能跳入语句块。
大数值BigInteger类
for each循环:
for(int element:a) System.out.println(element);
底层使用的是迭代器,所以在遍历的过程中,不能对集合中的元素进行增删。目标只能是Collection或者是数组。
Arrays.copyOf可以拷贝数组,如果直接b=a,拷贝的是数组地址值,改变b会改变a
第四章、对象和类
类之间最常见的关系有
依赖(uses-a):一个类的方法操纵另一个类的对象。尽可能的将相互依赖的类减至最小。
聚合(has-a):以为着类A的对象包含类B的对象
继承(is-a):构造器(构造方法)用来构造并初始化对象。通过new关键字构造一个新对象。构造器与其他方法有一个重要的不同,构造器总是伴随new操作符的执行被调用,而不能对一个已经存在的对象调用构造器来打到重新设置实例域的目的。构造器没有返回值
一个对象变量并没有实际包含一个对象,而仅仅引用一个对象,如:
Date deadline = new Date();
有两个部分。New Date()构造了一个Date类型的对象,它的值是新创建的对象的引用。这个引用储存在deadline中。
静态域、静态方法(用static修饰)伴随着类一直存在。静态方法通过类名调用。调用静态方法不需要创建实例(这也是main是静态方法的原因)。https://www.cnblogs.com/LZL-student/p/5861365.html,静态方法只能访问静态域。
在下面两种情况下使用静态方法:
一个方法不需要访问对象状态,其所需参数都是通过显示参数提供(如:Math.pow)
一个方法只需要访问类的静态域Java中方法参数的总结(Java中都是值传递,而不是引用传递):
一个方法不能修改一个基本数据类型的参数(数值型或布尔型)
一个方法可以改变一个对象参数的状态
一个方法不能让对象参数引用一个新的对象(Java中值传递的体现)当且仅当类没有提供任何构造器时,系统才会提供一个默认的构造器。
类设计技巧:P144
第五章、继承
this的两个主要用途:一是引用隐式参数,二是调用该类其他的构造器;
super的两个主要用途:一是调用超类的方法,二是调用超类的构造器。
调用构造器时,两者很相似,调用构造器的语句只能作为另一个构造器的第一条语句出现。Java不支持多继承。Java中多继承可通过接口实现。
多态、向上转型和向下转型:https://www.cnblogs.com/betterluo/p/10959239.html
父子类构造方法:子类构造方法必须调用父类构造方法。不写则有一个默认super()自动调用父类无参构造方法,如果父类没有无参构造方法,会报错,这时需在子类重载父类已有的有参数的构造方法即可。而且super重载必须是子类构造方法的第一句。
子类重写方法的返回值范围必须【小于等于】父类方法的返回值。例如父类返回值是void,子类是int,报错;父类返回值是Object,子类是String,不报错。
子类方法的权限必须【大于等于】父类方法的权限修饰符关于静态绑定和动态绑定:https://www.cnblogs.com/betterluo/p/10927746.html
final:一个方法被声明为final,子类不能覆盖重写这个方法。一个域被声明为final,构造对象之后不能改变值。一个类声明为final,只是其中的方法自动成为final,不包括类中的域。
内联:如果一个方法没有被覆盖并且很短,编译器就能够对它进行优化处理,这个过程称为内联。(挖个坑,以后学到JVM时来填)
抽象方法必须在抽象类中
抽象类除了抽象方法,还可以包含具体数据和具体方法。例如Person类可以保存姓名和一个返回姓名的具体方法
public abstract class Person { private String name; public Person(String name) { this.name = name; }
public abstract String getDescription(); public String getNmame(); { return name; } }
尽量将通用的域和方法(不管是否是抽象的)放在超类(不管是否是抽象类)中。
- 类即使不含抽象方法,也可以将类声明为抽象类。
抽象类不能被实例化,如果将一个类声明为abstract,就不能创建这个类的对象
注意:可以定义一个抽象类的对象变量,但是它只能引用非抽象子类的对象。例如 Person p = new Student("Vince Vu", "Economics"); //p是一个抽象类Person的变量,Person引用了一个非抽象子类Student的实例
可以理解为多态/向上转型
只有基本类型不是对象,例如:数值、字符、布尔类型的值。
Object中的equals方法,如果是基本类型比较的是值,如果是对象比较的是地址值。String重写了equals方法,比较的是两字符串的内容是否相等。https://www.cnblogs.com/dolphin0520/p/3592500.html。数组可以使用静态的Arrays.equals检测相应的数组元素是否相等。重写equals来比较两个对象的建议:P169。IDEA直接可以自动生成重写的equals和toString方法。。。
重写equals,记得参数列表为(Object other)。(重写:在继承中,方法的名称一样,参数列表也一样;重载:方法名称一样,参数列表不一样)
getClass()是使用反射技术来判断对象所属的类。不同的两个对象可能会有相同的散列码,所以重写equals就必须重写hashCode方法。P170。https://www.cnblogs.com/dolphin0520/p/3681042.html
toString是一种很好的调试工具。数组可通过Arrays.toString。建议每一个类都添加toString方法
自动装箱和拆箱:
有一个很有用的特性,便于添加int的元素到ArrayList<Integer>:
list.add(3);
将自动变换成
list.add(Integer.valueOf(3));
这种变化称为自动装箱(autoboxing)(基本类型的数据\->包装类)
相反地,当一个Integer对象赋给一个int值时,将会自动的拆箱(包装类\->基本类型的数据),包装类无法直接参与运算
int n = list.get(i);
自动变成
int n = list.get(i).intValue();
在算数表达式中也能够自动的装箱和拆箱
Integer n = 3;
n++;
编译器将自动地插入一条对象拆箱的指令,然后进行自增计算,最后再将结果装箱。
比较两个包装器对象时要调用equals,如果用==可能会出错。
包装器中包含了一些基本方法,如一个整数字符串转换成数值
int x = Integer.parseInt(s);
这里与Integer对象没有任何关系,parseInt是一个静态方法,Interger类只是放置这个方法的地方。
Integer.toString()可以把一个整数转换成字符串
Integer对象是不可变的:包含在包装器中的内容不会改变。所以
public static void triple(Integer x) // won't work { ... }
不会改变参数值。
org.omg.CORBA包中定义持有者(holder)类型,可以访问储存在其中的值,进而改变参数值。
参数数量可变的方法
1.一个方法只能有一个可变参数;2.如果方法的参数有多个,那么可变参数要写在参数的末尾 public static int max(int... values){ int lagest=0; for (int i = 0; i < values.length ; i++) { if(values[i]>lagest) lagest=values[i]; } return lagest; } //可变参数的特殊(终极)写法 public static void method(Object...obj){ }枚举类型实际上是一个类,例如
public enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE };
这个声明定义的类型是一个类,它刚好有4个实例。
比较两个枚举类型的值,永远不要用equals,用==。
枚举类型中可以添加一些构造器、方法和域。
- 反射(之后再回来看)
- Class类:Object中的getClass()方法可以返回一个Class类型的实例。
- 继承的设计技巧:P208
第六章、接口、lambda表达式与内部类
接口中的所有方法自动地属于public,声明时可省略。但是在实现接口时,必须把方法生命为public
接口中的变量其实是常量,接口不允许有实例域(常量)
实现Arrays中的sort方法:https://blog.csdn.net/qq_37856300/article/details/84940888
接口中的方法自动被设置为public,接口中的域自动设为public static final。java语言规范建议不要书写这些多余的关键字。
引用接口,而不是只用抽象类的原因是一个类只能继承一个类,但是可以实现多个接口
接口的默认方法与静态方法:https://www.cnblogs.com/dustcode/p/10007969.html 接口的默认方法可以通过接口实现类对象,直接调用
接口的默认方法可以被接口实现类覆盖重写
默认方法的主要优势是拓展接口的方法,而不破坏现有代码。解决默认方法冲突:
①超类优先:如果继承了一个类,实现了一个接口,那么超类优先
②接口冲突:如果实现了两个接口有想通的方法,必须通过覆盖来解决冲突lambda
内部类包括成员内部类、局部内部类和匿名内部类
如何使用成员内部类:
间接方式:
在外部类的方法中,使用内部类,在外部类方法中创建内部类对象,通过内部类对象调用内部类方法,main只是调用外部类的方法。通过外部类的对象,调用外部类的方法,里面间接再使用内部类
public class Outer {public class Inner{ public void show(){ System.out.println("内部类的方法"); } } public void method(){ System.out.println("外部类的方法"); Inner inner = new Inner(); //创建内部类对象 inner.show(); //通过内部类对象调用内部类方法 }
}
//通过调用外部类对象,调用外部类的方法 public class Main { public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } }
直接方式:
//类名称 对象名 = new 类名称();
//【外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();】
public class Main
{
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.show();
}
}
内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。(内用外,随意访问;外用内,需要先建立内部类对象,通过内部类对象访问)
没重名可直接调用外部类数据域,如果重名,通过【外部类名称.this.外部类成员变量名】来调用。 public class Outer {private int num = 100; public class Inner{ final int num = 200; public void show(){ int num = 300; System.out.println(num);//300 System.out.println(this.num);//200 System.out.println(Outer.this.num);//100 } }
}
局部内部类定义:如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。“局部”:只有当前所属的方法才能使用它,除了这个方法外面就不能用了。
访问局部内部类,是通过调用外部类包含该内部类的方法
public class Outer {public void methodOuter(){ class Inner { int num = 10; public void methodInner() { System.out.println(num); } } Inner inner = new Inner(); inner.methodInner(); } public static void main(String[] args) { Outer obj = new Outer(); obj.methodOuter(); }
}
类的权限修饰符:
public > protected > (default) >private
定义一个类的时候,权限修饰符:
①外部类:public / (default)
②成员内部类:public / protected / (default) / private
③局部内部类:什么都不能写局部内部类想访问所在方法的局部变量,这个局部变量必须是【有效final的】,即final或者没有更改过的
class Outer { public void methodOuter(){ final int num = 5; // num = 9; 错误 class Inner {
public void methodInner() { System.out.println(num); } }
} }匿名内部类:如果接口的实现类,(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略该类的定义,二改为使用【匿名内部类】。
/* 匿名内部类格式 接口名称 对象名 = new 接口名称(){ //覆盖重写所有抽象方法 }; //注意要有分号 / public static void main(String[] args){ //匿名内部类 MyInterface obj = new MyInterface(){ //new代表创建对象的动作 //接口名称是匿名内部类需要实现的接口 @Override //大括号中的内容才是匿名内部类的内容 public void method(){ } }; obj.method(); } / 注意: 1.匿名内部类,在【创建对象】的时候,只能使用唯一一次。如果希望多次创建对象,而且类的内容一样的话,那么就必须使用单独定义的实现类了。 2.匿名对象,在【调用方法】的时候,只能调用唯一一次。如果希望同一个对象,调用多次方法,那么必须给对象起一个名字。 3.匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】。匿名内部类和匿名对象不是一码事!!
匿名内部类作用:不用专门写一个类来实现接口,直接使用匿名内部类调用接口。
如果想重复使用,就不要用任何匿名的东西。不管是匿名内部类还是匿名对象。
//如果有两个对象,要写两遍
//使用匿名内部类
MyInterface objA = new MyInterface(){
@Override
public void method(){
System.out.println("匿名内部类实现了方法!");
}
};
objA.method();
MyInterface objB = new MyInterface(){
@Override
public void method(){
System.out.println("匿名内部类实现了方法!");
}
};
objB.method();
匿名对象:
//使用了匿名内部类,而且省略了对象名称,也是匿名对象
new MyInterface(){
@Override
public void method1(){
System.out.println("匿名对象匿名内部类实现了方法1");
}
@Override
public void method2(){
System.out.println("匿名对象匿名内部类实现了方法2");
}
}.method1();
//匿名对象只能调用一次方法
//如果还想调用method2还要创建一个新的匿名对象
new MyInterface(){
@Override
public void method1(){
System.out.println("匿名对象匿名内部类实现了方法1");
}
@Override
public void method2(){
System.out.println("匿名对象匿名内部类实现了方法2");
}
}.method2();
//如果想重复使用,就不要用任何匿名的东西
- 内部类是一种编译器现象,与虚拟机无关