Google 十年 Java 技术栈

1 java基础: 1.1 算法 1.1 排序算法:直接插入排序、希尔排序、冒泡排序、快速排序、直接选择排序、堆排序、归并排序、基数排序 1.2 二叉查找树、红黑树、B树、B+树、LSM树(分别有对应的应用,数据库、HBase) 1.3 BitSet解决数据重复和是否存在等问题 1.2 基本 2.1 字符串常量池的迁移 2.2 字符串KMP算法 2.3 equals和hashcode 2.4 泛型、异常、反射 2.5 string的hash算法 2.6 hash冲突的解决办法:拉链法 2.7 foreach循环的原理 2.8 static、final、transient等关键字的作用 2.9 volatile关键字的底层实现原理 2.10 Collections.sort方法使用的是哪种排序方法 2.11 Future接口,常见的线程池中的FutureTask实现等 2.12 string的intern方法的内部细节,jdk1.6和jdk1.7的变化以及内部cpp代码StringTable的实现 1.3 设计模式 单例模式 工厂模式 装饰者模式 观察者设计模式 ThreadLocal设计模式 1.4 正则表达式 4.1 捕获组和非捕获组 4.2 贪婪,勉强,独占模式 1.5 java内存模型以及垃圾回收算法 5.1 类加载机制,也就是双亲委派模型 5.2 java内存分配模型(默认HotSpot) 线程共享的:堆区、永久区 线程独享的:虚拟机栈、本地方法栈、程序计数器 5.3 内存分配机制:年轻代(Eden区、两个Survivor区)、年老代、永久代以及他们的分配过程 5.4 强引用、软引用、弱引用、虚引用与GC 5.5 happens-before规则 5.6 指令重排序、内存栅栏 5.7 Java 8的内存分代改进 5.8 垃圾回收算法: 标记-清除(不足之处:效率不高、内存碎片) 复制算法(解决了上述问题,但是内存只能使用一半,针对大部分对象存活时间短的场景,引出了一个默认的8:1:1的改进,缺点是仍然需要借助外界来解决可能承载不下的问题) 标记整理 5.8 常用垃圾收集器: 新生代:Serial收集器、ParNew收集器、Parallel Scavenge 收集器 老年代:Serial Old收集器、Parallel Old收集器、CMS(Concurrent Mark Sweep)收集器、 G1 收集器(跨新生代和老年代) 5.9 常用gc的参数:-Xmn、-Xms、-Xmx、-XX:MaxPermSize、-XX:SurvivorRatio、-XX:-PrintGCDetails 5.10 常用工具: jps、jstat、jmap、jstack、图形工具jConsole、Visual VM、MAT 1.6 锁以及并发容器的源码 6.1 synchronized和volatile理解 6.2 Unsafe类的原理,使用它来实现CAS。因此诞生了AtomicInteger系列等 6.3 CAS可能产生的ABA问题的解决,如加入修改次数、版本号 6.4 同步器AQS的实现原理 6.5 独占锁、共享锁;可重入的独占锁ReentrantLock、共享锁 实现原理 6.6 公平锁和非公平锁 6.7 读写锁 ReentrantReadWriteLock的实现原理 6.8 LockSupport工具 6.9 Condition接口及其实现原理 6.10 HashMap、HashSet、ArrayList、LinkedList、HashTable、ConcurrentHashMap、TreeMap的实现原理 6.11 HashMap的并发问题 6.12 ConcurrentLinkedQueue的实现原理 6.13 Fork/Join框架 6.14 CountDownLatch和CyclicBarrier 1.7 线程池源码 7.1 内部执行原理 7.2 各种线程池的区别 2 web方面: 2.1 SpringMVC的架构设计 1.1 servlet开发存在的问题:映射问题、参数获取问题、格式化转换问题、返回值处理问题、视图渲染问题 1.2 SpringMVC为解决上述问题开发的几大组件及接口:HandlerMapping、HandlerAdapter、HandlerMethodArgumentResolver、HttpMessageConverter、Converter、GenericConverter、HandlerMethodReturnValueHandler、ViewResolver、MultipartResolver 1.3 DispatcherServlet、容器、组件三者之间的关系 1.4 叙述SpringMVC对请求的整体处理流程 1.5 SpringBoot 2.2 SpringAOP源码 2.1 AOP的实现分类:编译期、字节码加载前、字节码加载后三种时机来实现AOP 2.2 深刻理解其中的角色:AOP联盟、aspectj、jboss AOP、Spring自身实现的AOP、Spring嵌入aspectj。特别是能用代码区分后两者 2.3 接口设计: AOP联盟定义的概念或接口:Pointcut(概念,没有定义对应的接口)、Joinpoint、Advice、MethodInterceptor、MethodInvocation SpringAOP针对上述Advice接口定义的接口及其实现类:BeforeAdvice、AfterAdvice、MethodBeforeAdvice、AfterReturningAdvice;针对aspectj对上述接口的实现AspectJMethodBeforeAdvice、AspectJAfterReturningAdvice、AspectJAfterThrowingAdvice、AspectJAfterAdvice。 SpringAOP定义的定义的AdvisorAdapter接口:将上述Advise转化为MethodInterceptor SpringAOP定义的Pointcut接口:含有两个属性ClassFilter(过滤类)、MethodMatcher(过滤方法) SpringAOP定义的ExpressionPointcut接口:实现中会引入aspectj的pointcut表达式 SpringAOP定义的PointcutAdvisor接口(将上述Advice接口和Pointcut接口结合起来) 2.4 SpringAOP的调用流程 2.5 SpringAOP自己的实现方式(代表人物ProxyFactoryBean)和借助aspectj实现方式区分 2.3 Spring事务体系源码以及分布式事务Jotm Atomikos源码实现 3.1 jdbc事务存在的问题 3.2 Hibernate对事务的改进 3.3 针对各种各样的事务,Spring如何定义事务体系的接口,以及如何融合jdbc事务和Hibernate事务的 3.4 三种事务模型包含的角色以及各自的职责 3.5 事务代码也业务代码分离的实现(AOP+ThreadLocal来实现) 3.6 ...

阅读全文 »

sqlalchemy 和 flask-sqlalchemy 分表

对于 sqlalchemy 和 flask-sqlalchemy 分表的方案,网上的资料很少,经过google和自己项目的尝试,找到比较合适的解决的方案。 下面的 user 表根据用户的ID % 100 分布在 0 - 99 张表中。 另外,如果要处理分库的话,只需要在model中加入 __bind_key__,等于分库的规则。 sqlalchemy 分表示例: #!/usr/bin/env python # -*- coding: utf-8 -*- from sqlalchemy import Column, Integer, String, BigInteger, SmallInteger from sqlalchemy import create_engine from sqlalchemy.orm import Session from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class UserData(object): _mapper = {} @staticmethod def model(user_id): table_index = user_id % 100 class_name = 'user_%d' % table_index ModelClass = UserData._mapper.get(class_name, None) if ModelClass is None: ModelClass = type(class_name, (Base,), dict( __module__=__name__, __name__=class_name, __tablename__='user_%d' % table_index, id=Column(BigInteger, primary_key=True), name=Column(String(255)), picture=Column(String(255)), valid=Column(SmallInteger), utime=Column(Integer), ctime=Column(Integer) )) UserData._mapper[class_name] = ModelClass cls = ModelClass return cls if __name__ == '__main__': engine = create_engine('mysql+pymysql://%s:%s@%s:%s/%s' % ('root', 'root', '127.0.0.1', '3306', 'test'), encoding='utf8') Base.metadata.create_all(engine) session = Session(engine) # user_id = 100001 user_id = 1000001 user = UserData.model(user_id) result_1 = session.query(user).count() result_2 = session.query(user).filter(user.name == "test", user.valid == 0).all() result_3 = session.query(user).filter_by(name="test", valid=0).all() print(result_1, result_2, result_3) flask-sqlalchemy 分表示例: #!/usr/bin/env python # -*- coding: utf-8 -*- from flask import Flask, jsonify from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config.from_pyfile('flask-sqlalchmey-sharding.cfg') db = SQLAlchemy(app) class User(object): '''用户分表''' _mapper = {} @staticmethod def model(user_id): table_index = user_id % 100 # db_index = int((user_id % 100) / 25) class_name = 'user_%d' % table_index ModelClass = User._mapper.get(class_name, None) if ModelClass is None: ModelClass = type(class_name, (db.Model,), { '__module__': __name__, '__name__': class_name, '__tablename__': 'user_%d' % table_index, 'id': db.Column(db.BigInteger, primary_key=True), 'name': db.Column(db.String(100)), 'phone': db.Column(db.String(20)), 'status': db.Column(db.SmallInteger, default=0), 'ctime': db.Column(db.Integer, default=0), 'utime': db.Column(db.Integer, default=0) }) User._mapper[class_name] = ModelClass cls = ModelClass return cls @app.route('/ /test', methods=['GET', 'POST']) def test(user_id): user = User.model(user_id) count = user.query.filter_by(id=user_id).count() item = user.query.filter(user.id == user_id, user.status == 0).first() # default name = item.name if item else "default" return jsonify(count=count, name=name) if __name__ == '__main__': app.run() flask-sqlalchmey-sharding.cfg 文件: SQLALCHEMY_DATABASE_URI = = 'mysql+pymysql://root:root@localhost:3306/test' SQLALCHEMY_ECHO = True SECRET_KEY = '\xfb\x12\xd ...

阅读全文 »

菜谱 14:从.zip文件中读取数据

Python 能够直接读取 zip 文件中的数据。我们现在需要实现这样一个小任务:直接读取一个 zip 文件,获取里面包含的文件列表以及每个文件的大小。

Python 的 zipfile 模块可以轻松地帮助我们解决这个任务:


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import zipfile

z = zipfile.ZipFile("11.zip", "r")
for filename in z.namelist():
        print filename
        bytes = z.read(filename)
        print len(bytes)

这里需要注意地是 zipfile 模块有些 zip 文件是无法处理,具体是里面插入了注释的 zip 文件或者多分卷的 zip 文件。

阅读全文 »

菜谱 13:在文件中搜索以及替换文本

使用命令行简单地替换一个文件中的文本内容,并且生成一个新的自定义文件名的文件。这是我们平时工作中常见的一个小任务,下面的这一段小代码能够轻松地完成这个任务:


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
usage = "usage: %s search_text replace_text [infilename [outfilename]]" % os.path.basename(
    sys.argv[0])

if len(sys.argv) < 3:
    print usage

else:
    stext = sys.argv[1]
    rtext = sys.argv[2]
    print "There are %s args " % len(sys.argv)

    if len(sys.argv) > 4:
        input = open(sys.argv[3])
        output = open(sys.argv[4], 'w')

        for s in input:
            output.write(s.replace(stext, rtext))

        input.close()
        output.close()

当我们使用 “python cookbook_13.py 1 a test.txt new.txt” 命令行的时候,test.txt 中 1 会被替换成 a,并且替换后的内容写入到 new.txt 中。

注意:infilename,outfilename 这两个参数没有的话,程序并不会报错,但是会输出类似 “There are ...” 的语句。如果命令行参数小于 3 的话,会输出 “usage:。。。”。


阅读全文 »

菜谱 12:使用 UDP 数据包发送消息

数据包发送短的文本消息实现是很简单的并且提供可一个非常轻量级的消息传递通道。但是这种模式有很大的缺陷,就是不保证的数据的可靠性,有可能会存在丢包的情况,甚至严重的情况就是服务器不可用的时候,会完全丢失你的消息。不过这个任务会在有些情况下十分有作用:
  • 你不关心消息是否丢失;
  • 你不想要终止程序只是因为消息无法传递;



#!/usr/bin/env python
# -*- coding: utf-8 -*-

# server.py
import socket
port = 8081
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", port))
print "waiting on port:", port
while 1:
    data, addr = s.recvfrom(1024)
    print data


# client.py
import socket
port = 8081
host = "localhost"
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", 0))
s.sendto("Holy Guido! It's working.", (host, port))

还有一个提醒事项,不要用上面的程序发送大量的数据包,尤其是在 Windows 上。要是想要发送大的消息的话,你可以这样做:

	BUFSIZE = 1024
	while msg:
	    s.sendto(msg[:BUFSIZE], (host, port))
	    msg = msg[BUFSIZE:]

阅读全文 »

菜谱 11:使用列表实现循环数据结构

在一些实际应用中,设计一个循环的数据结构是十分有利的。这里的循环数据结构指的是最后一个元素指向第一元素的结构。Python 内置的 list 能够很容易实现这个任务。


#!/usr/bin/env python
# -*- coding: utf-8 -*-


class Ring(object):

    def __init__(self, l):
        if not len(l):
            raise "ring must have at least one element"
        self._data = l

    def __repr__(self):
        return repr(self._data)

    def __len__(self):
        return len(self._data)

    def __getitem__(self, i):
        return self._data[i]

    def turn(self):
        last = self._data.pop(-1)
        self._data.insert(0, last)

    def first(self):
        return self._data[0]

    def last(self):
        return self._data[-1]

使用这个结构的方式:

	>>> l = [{1:1}, {2:2}, {3:3}]
	>>> r = Ring(l)
	>>> r
	[{1: 1}, {2: 2}, {3: 3}]
	>>> r.first()
	{1: 1}
	>>> r.last()
	{3: 3}
	>>> r.turn()
	>>> r
	[{3: 3}, {1: 1}, {2: 2}]
	>>> r.turn()
	>>> r
	[{2: 2}, {3: 3}, {1: 1}]

阅读全文 »

菜谱 10:统计单词出现的频率

平时我们在工作的时候需要统计一篇文章或者网页出现频率最高的单词,或者需要统计单词出现频率排序。那么如何完成这个任务了?


例如,我们输入的语句是 “Hello there this is a test.  Hello there this was a test, but now it is not.",希望得到的升序的结果:

[[1, 'but'], [1, 'it'], [1, 'not.'], [1, 'now'], [1, 'test,'], [1, 'test.'], [1, 'was'], [2, 'Hello'], [2, 'a'], [2, 'is'], [2, 'there'], [2, 'this']]

得到降序的结果是:

[[2, 'this'], [2, 'there'], [2, 'is'], [2, 'a'], [2, 'Hello'], [1, 'was'], [1, 'test.'], [1, 'test,'], [1, 'now'], [1, 'not.'], [1, 'it'], [1, 'but']]


完成这个结果的代码如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-


class Counter(object):

    def __init__(self):
        self.dict = {}

    def add(self, item):
        count = self.dict.setdefault(item, 0)
        self.dict[item] = count + 1

    def counts(self, desc=None):
        result = [[val, key] for (key, val) in self.dict.items()]
        result.sort()
        if desc:
            result.reverse()
        return result

if __name__ == '__main__':

    '''Produces:

        >>> Ascending count:
        [[1, 'but'], [1, 'it'], [1, 'not.'], [1, 'now'], [1, 'test,'], [1, 'test.'], [1, 'was'], [2, 'Hello'], [2, 'a'], [2, 'is'], [2, 'there'], [2, 'this']]
        Descending count:
        [[2, 'this'], [2, 'there'], [2, 'is'], [2, 'a'], [2, 'Hello'], [1, 'was'], [1, 'test.'], [1, 'test,'], [1, 'now'], [1, 'not.'], [1, 'it'], [1, 'but']]
    '''

    sentence = "Hello there this is a test.  Hello there this was a test, but now it is not."
    words = sentence.split()
    c = Counter()
    for word in words:
        c.add(word)
    print "Ascending count:"
    print c.counts()
    print "Descending count:"
    print c.counts(1)

阅读全文 »

菜谱 9:soundex 算法

SOUNDEX 返回由四个字符组成的代码 (SOUNDEX) 以评估两个字符串的相似性。返回的第一个字符是输入字符串的第一个字符,返回的第二个字符到第四个字符是数字。

soundex 代码如下:


#!/usr/bin/env python
# -*- coding: utf-8 -*-


def soundex(name, len=4):
    """ soundex module conforming to Knuth's algorithm
        implementation 2000-12-24 by Gregory Jorgensen
        public domain
    """

    # digits holds the soundex values for the alphabet
    digits = '01230120022455012623010202'
    sndx = ''
    fc = ''

    # translate alpha chars in name to soundex digits
    for c in name.upper():
        if c.isalpha():
            if not fc:
                fc = c   # remember first letter
            d = digits[ord(c) - ord('A')]
            # duplicate consecutive soundex digits are skipped
            if not sndx or (d != sndx[-1]):
                sndx += d
    print sndx

    # replace first digit with first alpha character
    sndx = fc + sndx[1:]

    # remove all 0s from the soundex code
    sndx = sndx.replace('0', '')

    # return soundex code padded to len characters
    return (sndx + (len * '0'))[:len]

需要注意的是代码设计为处理英文名称。

阅读全文 »

菜谱 8:支持简单命令行

本任务最初的目的只是为了在测试过程中使用简单的命令行运行不同的函数,类似运行 “python test_test.py” 运行整个测试,运行 “python test_test.py debug” 来运行测试但是不收集运行结果,请看如下的代码:


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import unittest
import sys


class Tests(unittest.TestCase):

    def testAddOnePlusOne(self):
        assert 1 == 2


def main():
    unittest.TextTestRunner().run(test_suite())


def test_suite():
    return unittest.makeSuite(Tests, 'test')


def debug():
    test_suite().debug()

if __name__ == '__main__':

    if len(sys.argv) > 1:
        globals()[sys.argv[1]]()
    else:
        main()

这里如果在命令行中直接运行 “python cookbook_8.py” 就会执行 “main()”;如果在命令行中运行 “python cookbook_8.py debug” 会执行 “debug()”。

“globals()” 返回的是当前全局变量的引用。如果有其它的需求,可以充分利用本任务来延伸!


阅读全文 »

菜谱 7:发送混合邮件

我们平时需要使用 Python 发送各类邮件,这个需求怎么来实现?答案其实很简单,smtplib 和email 库可以帮忙实现这个需求。smtplib 和 email 的组合可以用来发送各类邮件:普通文本,HTML 形式,带附件,群发邮件,带图片的邮件等等。我们这里将会分几节把发送邮件功能解释完成。

smtplib 是 Python 用来发送邮件的模块,email 是用来处理邮件消息。

发送邮件系列最后一篇将会介绍发送混合邮件:里面包含附件,HTML 形式,不同文本:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

sender = '***'
receiver = '***'
subject = 'python email test'
smtpserver = 'smtp.163.com'
username = '***'
password = '***'

# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('mixed')
msg['Subject'] = "Link"

# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttp://www.python.org"
html = """\

 
Hi!

       How are you?

       Here is the link you wanted.

 

"""

# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')

# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)
# 构造附件
att = MIMEText(open('/Users/1.jpg', 'rb').read(), 'base64', 'utf-8')
att["Content-Type"] = 'application/octet-stream'
att["Content-Disposition"] = 'attachment; filename="1.jpg"'
msg.attach(att)

smtp = smtplib.SMTP()
smtp.connect(smtpserver)
smtp.login(username, password)
smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()

注意:这里的代码并没有把异常处理加入,需要读者自己处理异常。

阅读全文 »