1.权限控制 2.类创建者修改某些实现而不会影响类使用者
1.继承 2.组合(composition UML中实心菱形+实线表示)
(UML中空心三角+实心线表示)
父类引用指向子类对象 ClassParent classA = new ClassChild(); 也就是将子类泛化成父类类型 在新增一个继承父类的子类类型时,可以轻松替换掉原来的子类类型,程序更新维护便利,在实际classA调用方法的时候编译器并不知道要调用的代码在什么地方,是在运行时才知道调用子类的对应方法的,这也称为后期绑定。
将子类看作是基类的过程称为向上转型(upcasting)
方向相反则是向下转型,向下转型可能导致ClassCastException,因为就像是一个形状不一定是圆,它也可能是矩形或者三角形.而参数化类型:泛型正是为此而生
多重继承很灵活但是复杂,而单根继承相对简单,效率更高。单根继承类型是确定的,这样优化了垃圾回收;垃圾回收往往因为不确定的类型而陷入僵局。
List(存储序列)Map(关联数组,建立Key value关系)Set(内容不可重复,实现基于map)队列 树 堆栈等
C++对象的生命周期和存储位置可以在编写程序时决定,但是这样必须知道对象的确切数量生命周期和类型,在解决一般化或者特别复杂的问题时不够灵活。
Java则在堆(heap)内存池中动态创建对象。知道运行时才知道需要多少对象,生命周期如何,具体是什么类型。只有程序运行到相关代码行才能确定这一切。如果需要一个对象,可以随时在堆中创建,这是因为存储空间在运行时时动态管理的,也因此一般比在堆栈中创建和销毁对象需要更多的时间。
动态方式在创建对象时具有灵活性,所以适用于复杂系统。但是效率可能就没有C++高了。
因为Java采用了动态堆上分配内存的机制,因此Java垃圾回收可以自动回收,而像C++手动堆栈分配内存就没法做到自动回收,程序员手动回收时则容易出现遗漏后者错误
在从错误状态进行可靠恢复的途径
优点:多处理器同时执行任务,效率高 缺点:共享资源时存在隐患
网络编程
客户端编程与服务器编程
本章主要讲述Java的一些重要特点
(书中总结的很好)
运算速度最快,但是数量有限,在Java中是不可直接控制的。从程序中无法感觉寄存器的存在
位于RAM(随机访问存储)中,多用于存储对象的引用,依靠指针的上下移动来工作。指针上移,便是释放一个对象,指针下移,表示分配一个新内存。因为需要上下移动指针,所以Java系统需要知道存储在堆栈中所有对象的确切生命周期。对战的速度仅次于寄存器。注意堆栈中存储了对象的引用但没有将实际对象存储在这里
也位于RAM中,用于存放Java对象。堆与堆栈的区别主要在于1.他们存储的内容不同2.堆不需要也不知道堆中存储的数据需要存活多久。当使用new新建一个对象时,堆中自会给其分配内存。因此相比于堆栈,堆中的内存分配更加灵活,但是因为这种灵活,也需要付出代价:堆中的内存分配和释放需要更多的时间
根据不同系统而不同,有的系统将常量存放在程序代码内部;而有的系统则会将常量与其他部分分离,在此情况下,可以将常量存放于ROM(只读存储器)–典型的例子是字符串常量池
数据可以脱离程序而存在,此类的两个典型是流对象和持久化对象。流对象通常发生在一个机器与另一个机器的数据传输,对象被转化成字节流或者字符流传输给另外一个对象。持久化对象则被存储与磁盘,需要时则可以将他们解析和转换成基于RAM,可恢复的常规的程序对象。
特例,基本类型的存储
因为使用new创建对象需要在堆栈生成一个引用,在堆中动态分配内存,对象如果比较小,往往效率不高。8大基本类型在堆栈中创建的变量存放的不是引用而直接存放值,这样就不必在堆中分配内存,因此基本类型的创建更为灵活。
当然基本数据类型都有与之对应的包装类,而包装类的创建对象的形式则和普通对象的创建方式一样。比如
Integer I = new Integer(10);
User user = new User();
在堆栈和堆中创建变量,分配内存的形式是一样的
数组在C与C++中是一个连续的内存块,而在Java中,创建数组则意味着创建了一个引用数组,并且每一个引用初始化为null
变量的作用域由花括号决定。
举个例子
{
String s = new String(“aaa”);
}
其中变量s的作用域在后花括号之后就消失了,但是s所指向的String对象仍然存在于堆栈中。在C++中,这样的对象积累起来,会导致内存占用,这会引起内存泄漏但是Java由自己的垃圾回收机制,不需要程序员手动处理类似问题,这样就避免嘲笑我忘记释放内存而产生问题。
类的字段可以不进行初始化,Java会有一些默认初始化
类的字段如果是一个引用,则没有初始化,其值默认初始化为null.
而方法中的变量必须初始化,若非如此,Java会提示错误 变量没有初始化。
使用包名区分
运用import导包
类的static变量对于该类的所有对象来说只存在一个实例(static字段堆每个类来说只有一份存储空间),该变量可以直接使用类名获取其值,也就是说即使没有创建对象,也可以调用该变量。被static修饰的方法和变量在一些文献中也叫类方法和类变量。
1.安装JDK并配置好环境变量(目的是能找到java和javac两个命令)
2.javac Xxx.java
3.java Xxx
常见的单行 多行注释不需多说,本小节主要说的是Javadoc这个注释提取工具,在java文件中注释以一定格式编写,则使用javadoc可以提取注释并生成另外的文档,一般可以使用浏览器打开类似的文档,撰写注释是务必遵守一些语法,还会使用到一些html标签和@开头的javadoc标签(@See @link之类),在实际中其实用的比较少,但是我们可以看到我们使用的JDK的注释中确实出现类似描述的东西。由于用的比较少,就不详述了。
驼峰式为主
本章主要讲述要创建第一个Java程序需要的要素,比如项目构建需要创建包名,需要导包,需要创建方法,变量,使用静态方法,如何注释并通过javadoc生成html文档等等
其实就是引用传递的问题,问题出现在赋值和方法调用上
比如
public class Book {
public Book(String name) {
super();
this.name = name;
}
String name;
private void changeString(String s) {
s = "zzz";
}
private void changeString(Book book) {
book.name = "zzz";
}
public static void main(String [] args){
Book book1 = new Book("Java");
Book book2 = new Book("Android");
System.out.println(book1.name);
System.out.println(book2.name);
book1 = book2;//赋值产生的引用传递 这句是问题关键
book1.name = "Java Script";
System.out.println(book1.name);
System.out.println(book2.name);
book2 = book1;
book1.name = "Python";
System.out.println(book1.name);
System.out.println(book2.name);
System.out.println();
//参数传递时的引用传递
String s = "abc";
book1.changeString(s);//调用changeString方法
System.out.println(s);
String bookName = "C++";
Book book = new Book(bookName);
book.changeString(book);//调用changeString(Book)方法
System.out.println(bookName);
System.out.println(book.name);
//核心: 基本类型赋值和方法调用都不会发生引用传递,都是拷贝值,只有引用类型的变量才有这两个问题。
//这里应该时问题的引入,不理解也没关系,第六章应该会有详述
}
}
输出
Java
Android
Java Script
Java Script
Python
Python
abc
C++
zzz
其优先级不需要死记硬背,有时候不记得可以使用括弧,而且这样程序的可读性更高。
对于++i --i i++ i–之类的操作符也很简单,不赘述
直接用== !=判断即可,比如
public static void main(String[] args) {
String string1 = "sd";
String string2 = "sd";
System.out.println(string1 == string2);
}
输出true
对于Java已经定义好的引用类型(如Integer String Boolean)大多数可以使用equals来判断内容是否相等
public static void main(String[] args) {
String string1 = new String("sd");
String string2 = new String("sd");
System.out.println(string1 == string2);
System.out.println(string1.equals(string2));
Boolean boolean1 = new Boolean(false);
Boolean boolean2 = new Boolean(false);
System.out.println(boolean1 == boolean2);
System.out.println(boolean1.equals(boolean2));
Integer integer1 = new Integer(15);
Integer integer2 = new Integer(15);
System.out.println(integer1 == integer2);
System.out.println(integer1.equals(integer2));
}
输出
false
true
false
true
false
true
可以看到 “== ”用在引用类型上,输出结果可能与想象的不同,这是因为对于引用类型,“”判读的是 对象的引用是否相等,当我们使用new创建对象时,java往往分配不同的堆栈和堆空间给不同的对象,因此对于引用类型,使用“”判断往往返回false。
但并不是只要是引用类型,只要使用equals就可以判断其内容是否等价。当比较对象类型是自定义类型时,需要我们覆盖Object的equals方法才可以正确比较,否则调用equals方法会默认调用Object的equals方法,而它的实现就是使用“==”来比较,这一点在第17章将会详述。
与或非(&& || !)比较简单,就略过了,但是书中也讲述了&&和& 以及|| 和|的关系,实际应用时,似乎|和&很少使用,关键词是短路,简单,略过。
此处没搞明白,说是直接变量或许更好?这里说的最多的一个是定义数字类型时加前缀后缀的问题,这个用的其实很少。另外一个是“窄化转型”,在本章后面有讲述。
这用的也特别少,这里Think in Java还是很严谨的,详述了Java中e与科学自然e的区别。
这个用的也比较少,一共四种 按位与& 按位或| 按位非~ 按位异或^,他们和逻辑操作符极为类似。
一个例子
public static void main(String[] args) {
int i = 1 + 4 + 16 + 64;
int j = 2 + 8 + 32 + 128;
System.out.println("i = " + Integer.toBinaryString(i));
System.out.println("j = " + Integer.toBinaryString(j));
System.out.println("i & j = " + Integer.toBinaryString(i & j));
System.out.println("i | j = " + Integer.toBinaryString(i | j));
System.out.println("i ^ j = " + Integer.toBinaryString(i ^ j));
System.out.println("~i = " + Integer.toBinaryString(~i));
System.out.println("~j = " + Integer.toBinaryString(~j));
}
结果
i = 1010101
j = 10101010
i & j = 0
i | j = 11111111
i ^ j = 11111111
~i = 11111111111111111111111110101010
~j = 11111111111111111111111101010101
分左移位操作符<< 右移位操作符>> 无符号右移>>> 这个我也不经常使用,其他书籍资料中讲述位运算对于机器来说,其速度比普通的加减乘除快,所以很多地方为了提高运算速度使用移位,比如左移一位一般是2 两位4,三位*8……
这里的移位和按位操作符结合起来,还是很复杂的,但其实用的不多,至于掌握到什么程度,就看个人需要了。
就是if else的简化版,只能由一个else,看起来代码简洁些。在Android studio中,如果可以使用但没有使用三元操作符的地方会有警告,但书中以及个人也觉得if else的逻辑可读性更高,何时使用三元操作符就看个人了
这个用的就多了,不过也很简单,就是如果一个表达式以字符串起头,后面所有操作数都会自动转成字符串类型
例子
public static void main(String[] args) {
int x = 1;
int y = 3;
String string = "string";
System.out.println(x + y + string);
System.out.println(x + string + y);
System.out.println(string + x + y);
}
输出
4string
1string3
string13
byte8位 short16位 int32位 long64位 float32位 double64位
char16位
日常赋值和计算时会有些类型的转换,这种情况发生在
1.不同类型的变量之间的赋值
2.不同类型的变量之间的运算
第一点例如:
int x =1;
float y = 2.02f;
System.out.println(x +" "+ y );
y = x;
System.out.println(x +" "+ y );
y = (float)x;//强制转换其实是不必要的,会自动转换
System.out.println(x +" "+ y );
第二点比如int类型与float类型运算,结果会是float类型
float类型与double类型运算,结果会是double类型
将int值赋值给float变量,值会自动转型成float类型,就像执行了强制转换,这种自动的转换在本书中称为
类型提升,即将一个小容器里的东西放到大容器中。但反过来则不行
将float值赋值给int变量,会报错,除非加上强制转换,可以理解为Java认为这样做会丢失精度,是不安全的,需要使用者自己知道。这就相当于将大容器的东西放到小容器,小容器有可能放不下,那么只能把物体裁小(精度丢失),才能放入小容器,就出现了截尾。
比如将29.7赋值给int值,则最后的值是29
想要使用四舍五入要使用Math类的round方法
1.如果不确定操作符的运算顺序,就使用括弧,这样可以提高可读性和确保代码正确性
2.(个人想法,不是书中建议)如果没有必要,尽量避免不同类型数值之间的运算,虽然类型转换有一定的规律可循,但是我们应当避免无意的错误或者精度丢失。这样的错误往往是难以发现的。
char byte short运算时有时会被操作符进行类型提升(转化成int值)之后需要自行窄化(强制转换成原来的类型,可能信息丢失)
相同类型的数据进行运算时要注意可能的超出范围
bool类型的数据在java中的功能很有限,很多在c c++中可以进行的操作在Java中是行不通的